Skip to content

Here's a structured breakdown of potential flaws, concerns, and improvements in the code. #36

@coryscotthodson-png

Description

@coryscotthodson-png

⚠️ Issues and Fixes

  1. Incorrect Power Indexing in __shamirFn

Problem:

for (let a of q) r = S.__GF256_add(r, S.__GF256_mul(a, S.__GF256_pow(x, q.indexOf(a))));

q.indexOf(a) is not safe in case of duplicate values — it returns the first index.

Polynomial evaluation should use actual index position, not indexOf.

✅ Fix:

for (let i = 0; i < q.length; i++) {
r = S.__GF256_add(r, S.__GF256_mul(q[i], S.__GF256_pow(x, i)));
}


  1. Broken Lagrange Interpolation Denominator

Problem in __shamirInterpolation:

let a = points[m][0];
let b = S.__GF256_add(points[j][0], points[m][0]); // <-- incorrect
let c = S.__GF256_div(a, b);

This deviates from the standard Lagrange interpolation, where:

L_j(x) = \prod_{m \ne j} \frac{x - x_m}{x_j - x_m}

✅ Fix:

let numerator = S.__GF256_sub(0, points[m][0]); // x = 0 for secret recovery
let denominator = S.__GF256_sub(points[j][0], points[m][0]);
let fraction = S.__GF256_div(numerator, denominator);
p_j_x = S.__GF256_mul(p_j_x, fraction);


  1. Random Coefficients May Include Duplicates

Problem:

do {
w = e[ePointer++];
} while (q.includes(w));

You're rejecting duplicate coefficients unnecessarily. Shamir's scheme allows duplicate y-values, and it's unlikely to matter for securely chosen randomness.

This can also reduce entropy or cause rejection loops.

✅ Optional Fix:

Remove this rejection unless you have a specific reason to avoid duplicates in coefficients.


  1. Ambiguous Bit Mask in Share Indexing

Problem:

let index_mask = 2 ** indexBits - 1;

You're using indexBits=8 by default, which is fine, but if someone uses fewer bits, the randomness space shrinks.

Might be okay depending on use-case, but it's worth noting for security guarantees.


✅ Summary Fixes (Required)

Fix Description

✅ __shamirFn Use loop index, not indexOf()
✅ __shamirInterpolation Use sub, not add, for Lagrange denominator
⚠️ Random coeffs Consider allowing duplicates unless provable issue
⚠️ Share index mask Ensure entropy remains secure if bits reduced

Input:

threshold: minimum number of shares needed to reconstruct.

total: total number of shares to generate.

secret: the secret split byte-by-byte.

indexBits: how many bits to use for the x-coordinate (defaults to 8).

It generates total number of shares with unique non-zero x-coordinates.

For each byte of the secret:

Constructs a random polynomial of degree threshold - 1, with secret byte as the constant term.

Evaluates this polynomial at all chosen x-values to produce shares.


⚠️ Critical / Possible Bugs or Concerns

  1. Invalid index selection due to indexBits logic

let index_mask = 2**indexBits - 1;
if (total > index_mask) throw new Error("index bits is to low");

Should be:

if (total > index_mask + 1) throw new Error("index bits too low");

Reason: index_mask is the maximum value, and you skip index 0, so the number of usable indexes is only index_mask.

Fix suggestion:

if (total > index_mask) throw new Error("index bits too low (max usable: " + index_mask + ")");


  1. index !== 0 check: Why is index 0 skipped?

if ((shares[index] === undefined)&&(index !== 0)) {

Skipping index === 0 may break polynomial interpolation if 0 is a valid input (which it usually is). In classic Shamir's Secret Sharing, x = 0 is often used as the secret.

If you're avoiding x=0 deliberately (e.g., to avoid exposing the secret directly), document this explicitly.

However, this introduces bias and reduces the available index pool.

✅ Skipping x=0 is valid if and only if your Shamir reconstruction algorithm accounts for that and expects only x > 0.


  1. q.includes(w) check reduces entropy

do {
...
} while (q.includes(w));

This ensures coefficients are unique, but in standard SSS, polynomial coefficients do not need to be unique.

You're reducing entropy unnecessarily by rejecting duplicates.

Fix suggestion: Remove the q.includes(w) check for more entropy and better randomness.


  1. Ambiguous / unclear functions:

You use:

BF([])

BC([...])

S.__shamirFn(i, q)

These are likely:

BF = byte field / finite field byte conversion

BC = byte concatenation?

S.__shamirFn(x, coeffs) = evaluate polynomial

If these are not secure finite field arithmetic functions over GF(256), then your SSS implementation may break or become insecure.

Ensure: All arithmetic is done in the correct finite field (GF(256)), not modulo 256 unless using proper irreducible polynomial.


  1. Re-using entropy multiple times

You're generating entropy once and consuming it gradually:

e = S.generateEntropy({hex:false});
...
if (ePointer >= e.length) {
e = S.generateEntropy({hex:false});
ePointer = 0;
}

This is fine if S.generateEntropy gives truly random, cryptographically secure bytes. Just be aware of overusing small entropy pools.

✅ Use a cryptographically secure RNG and re-seed adequately.


  1. Returned structure is unclear

You return:

return shares;

But shares is an object where each key is the x-coordinate, and value is... a buffer?

This makes sense, but you may want to return it as:

return sharesIndexes.map(i => ({ x: i, y: shares[i] }));

For better serialization and reconstruction clarity.


🧠 Suggested Improvements

Add type validation for inputs.

Consider not skipping index 0 (or explicitly document why you are).

Don't enforce uniqueness of polynomial coefficients.

Clarify or rename BF and BC functions.

Return shares as { x, y } tuples, not an object map.

Add test vectors (e.g., compare with known SSS implementations).


✅ Example of improvement

if (threshold > 255 || total > 255)
throw new Error("threshold and total must be <= 255");
if (threshold > total)
throw new Error("threshold cannot exceed total");
const index_mask = 2 ** indexBits - 1;
if (total > index_mask)
throw new Error("indexBits too low to generate unique indices");

const shares = {};
const shareIndices = [];
let entropy = S.generateEntropy({ hex: false });
let ePointer = 0;

while (shareIndices.length < total) {
if (ePointer >= entropy.length) {
entropy = S.generateEntropy({ hex: false });
ePointer = 0;
}
const index = entropy[ePointer++] & index_mask;
if (index !== 0 && !(index in shares)) {
shares[index] = BF([]);
shareIndices.push(index);
}
}

entropy = S.generateEntropy({ hex: false });
ePointer = 0;

for (let b = 0; b < secret.length; b++) {
const coeffs = [secret[b]];
for (let j = 1; j < threshold; j++) {
if (ePointer >= entropy.length) {
entropy = S.generateEntropy({ hex: false });
ePointer = 0;
}
coeffs.push(entropy[ePointer++]); // allow duplicates
}
for (const i of shareIndices) {
const y = S.__shamirFn(i, coeffs);
shares[i] = BC([shares[i], BF([y])]);
}
}

return shareIndices.map(i => ({ x: i, y: shares[i] }));


Btc address is bc1qtvxzxt2pq20xd27pyarjhwv2ve8pl93e06qjy4

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions