Consider a scenario in which you are tasked with managing the security of a bank’s vault. The vault is considered impenetrable without a key, which you are given on your first day on the job. Your goal is to securely manage the key.

Let us suppose that you decide to keep the key on you at all times, granting access to the vault on an as-needed basis. You quickly learn that such a solution doesn’t scale well in practice, as your physical presence is required whenever the vault needs to be opened. What about those vacation days you were promised? Also, perhaps more frighteningly, what if you lost the one and only key?

With your vacation days in mind, you decide to make a copy of the key and entrust it with another employee. However, you realize that this isn’t ideal either. In doubling the number of keys, you have also doubled the number of opportunities for a thief to steal the vault’s key.

Out of desperation, you destroy the duplicate and decide to split the original key in half. Now, you think, two trusted people must be physically present with each key shard to reassemble the key and unlock the vault. This means that a thief would need to steal *two* key shards to reassemble the original key, which is double the amount of work of stealing one key. However, you soon realize that this scheme isn’t much better than simply having one key, because if *either* of you were to lose a key half then the full key cannot be recovered anyway.

This problem can be solved with a series of additional keys and locks, but can quickly require *a lot* of keys and locks when approached this way. You decide that an ideal key management scheme must involve splitting up a key, so that the security of the key isn’t fully entrusted with one person. You also conclude that some *threshold* of key shards should be required for key reassembly, so that one person losing their key shard (or going on vacation) doesn’t render the entire key unrecoverable.

## How to Share a Secret

This is the type of key management scheme that Adi Shamir was thinking about in 1979 when he published How to Share a Secret. The paper concisely explains a so-called \((k,n)\) threshold scheme that can be used to efficiently split up a secret value (e.g. a cryptographic key) into \(n\) shares. Then, if and only if at least \(k\) of any \(n\) shares are collected, it should be easy to recover the secret, \(S\).

An important security goal of this scheme is that an attacker should learn absolutely nothing if they don’t have at least \(k\) shares. Even \(k - 1\) shares should provide no information. We call this property *semantic security*.

## Polynomial Interpolation

Shamir’s \((k,n)\) threshold scheme is built around the concept of *polynomial interpolation*. If you’re not familiar with this concept, it’s actually quite simple. In fact, if you’ve ever plotted points on a graph and then drew lines or curves to connect them, you’ve already used it!

Consider a polynomial function with a degree of one, \(f(x) = x+2\). If you wanted to plot this function on a graph, how many points would you need? Well, we know this is a linear function, which forms a line and therefore needs at least two points. Next let us consider a polynomial function with a degree of two, \(f(x) = x^2 +2x + 1\). This is a quadratic function, and therefore requires at least three points to plot on a graph. How about a polynomial with a degree of three? At least four points. And so on and so forth.

The really neat thing about this property is that given the degree of a polynomial function and at least \(degree + 1\) points, we can plot additional points for that polynomial function. We call extrapolating those additional points *polynomial interpolation*.

## Plotting a Secret

You may already see Shamir’s clever scheme beginning to play out here. Let us suppose that our secret, \(S\), is \(42\). We can turn \(S\) into a point on a graph, \((0,42)\), and come up with a polynomial function with a \(k-1\) degree that satisfies this point. Recall that \(k\) is going to be our threshold of required shares, so if we want a threshold of three shares, we should choose a polynomial function with a degree of two.

Our polynomial function is going to take the form \(f(x) = a_0+a_1x+a_2x^2+a_3x^3+…+a_{k-1}x^{k-1}\), where \(a_0 = S\) and \(a_1,…,a_{k-1}\) are randomly chosen positive integers. All we’re doing is constructing a polynomial function with a \(k-1\) degree, where the free coefficient, \(a_0\), is our secret, \(S\), and the proceeding \(k-1\) terms each have a randomly chosen positive coefficient. If we go back to our original example and suppose that \(S = 42, k = 3, a_1,…,a_{k-1} = [3,5]\), then we get the function \(f(x) = 42 + 3x + 5x^2\).

At this point we are free to generate our shares by plugging \(n\) unique integers into \(f(x) = 42 + 3x + 5x^2\) where \(x \neq 0\) (since that’s our secret). In this example we want to distribute four shares with a threshold of three, so we randomly generate the points \((18, 1716), (27, 3768), (31, 4940), (35, 6272)\) and send one point to each of our trusted four individuals. We also inform the individuals that \(k = 3\), since this is considered public information and is needed to reconstruct \(S\).

## Reconstructing a Secret

We already discussed the concept of polynomial interpolation, and how it is the very foundation of Shamir’s \((k,n)\) threshold scheme. When any three of our four entrusted individuals want to reconstruct \(S\), all they need to do is interpolate \(f(0)\) with their unique points. To achieve this they can define their points, \((x_1,y_1),…,(x_k,y_k) = (18, 1716), (27, 3768), (31, 4940)\), and calculate a Lagrange interpolating polynomial using the following formula. If you’re more comfortable with programming than mathematics, the pi is basically a `for`

statement that multiplies all the results together, and the sigma a `for`

statement that adds all of the results together.

\[ P(x) = \sum_{j=1}^{k} p_j(x) \]

\[ P_j(x) = y_j \prod_{\scriptstyle m=1\atop\scriptstyle m\neq j}^{k}\frac{x-x_m}{x_j-x_m} \]

When \(k = 3\), we can solve this like so and get back our original polynomial function:

\[ \begin{aligned} P(x) &= {y_1}\left({x-x_2 \over x_1-x_2}\cdot{x-x_3 \over x_1-x_3}\right) + {y_2}\left({x-x_1 \over x_2-x_1}\cdot{x-\_3 \over x_2-x_3}\right) + {y_3}\left({x-x_1 \over x_3-x_1}\cdot{x-x_2 \over x_3-x_2}\right) \\ P(x) &= {1716}\left({x-27 \over 18-27}\cdot{x-31 \over 18-31}\right) + {3768}\left({x-18 \over 27-18}\cdot{x-31 \over 27-31}\right) + {4940}\left({x-18 \over 31-18}\cdot{x-27 \over 31-27}\right) \\ P(x) &= 42 + 3x + 5x^2 \end{aligned} \]

Since we know \(S = P(0)\), reconstructing \(S\) is simple:

\[ \begin{aligned} P(0) &= 42 + 3(0) + 5(0)^2 \\ P(0) &= 42 \end{aligned} \]

## Exploiting Insecure Integer Arithmetic

Although we’ve successfully applied the fundamental idea behind Shamir’s \((k,n)\) threshold scheme, we have a remaining problem that we’ve ignored up until now. Our polynomial function uses insecure integer arithmetic. Consider that for every additional point an attacker obtains on our function’s graph, the smaller number of possibilities remain for what other points can be. You can begin to see this deduction visually as you graph an increasing number of points for a polynomial function using integer arithmetic. This is counterproductive to our stated security goal that an attacker should learn absolutely nothing if they don’t have at least \(k\) shares.

To demonstrate how weak the scheme is when integer arithmetic is used, consider a scenario in which an attacker has obtained two points, \((18, 1716), (27,3768)\), and knows the public information that \(k = 3\). From this information they can derive the degree of \(f(x)\), which is two, and plug in known \(x\) and \(f(x)\) values.

\[ \begin{aligned}f(x) &= a_0 + a_1x + a_2x^2 + a_3x^3 + … + a_{k-1}x^{k-1} \\ f(x) &= S + a_1x + a_2x^2 \\ f(18) \equiv 1716 &= S + a_118 + a_218^2 \\ f(27) \equiv 3768 &= S + a_127 + a_227^2\end{aligned} \]

An attacker can then solve for \(a_1\) by evaluating \(f(27) - f(18)\):

\[ \begin{aligned}3768 - 1716 &= (S - S) + (27a_1 - 18a_1) + (729a_2 - 324a_2) \\ 2052 &= 9a_1 + 405a_2 \\ 9a_1 &= 2052 - 405a_2 \\ a_1 &= \frac{2052 - 405a_2}{9} \\ a_1 &= 228 - 45a_2\end{aligned} \]

Since we defined \(a_1,…,a_{k-1}\) to be randomly chosen positive integers, there are only so many possibilities for what \(a_2\) can be. With this information, an attacker can deduce \(a_2 \in [1,2,3,4,5]\), since anything greater than 5 would make \(a_1\) negative. This turns out to be true since we defined \(a_2 = 5\).

From there an attacker can begin to deduce possible values of \(S\), by replacing \(a_1\) in \(f(18)\):

\[ \begin{aligned}1716 &= S + a_118 + a_218^2 \\ 1716 &= S + 18\left(228 - 45a_2\right) + a_218^2 \\ 1716 - S &= 18\left(228 - 45a_2\right) + a_218^2 \\ -S &= 18\left(228 - 45a_2\right) + a_218^2 - 1716 \\ -S &= 4104 - 810a_2 + a_218^2 - 1716 \\ S &= -4104 + 810a_2 - a_218^2 + 1716 \\ S &= 810a_2 - 324a_2 -2388 \\ S &= 486a_2 - 2388\end{aligned} \]

With the limited set of possibilities for what \(a_2\) can be, as deduced by the attacker earlier, it’s clear how easy it would be to guess and check for values of \(S\). There are only five possibilities.

## Addressing Insecure Integer Arithmetic

In order to address this vulnerability, Shamir proposes that we use modulo arithmetic, changing \(f(x)\) to \(f(x) \mod p\) where \(p \in \mathbb{P} : p > S, p > n\) and \(\mathbb{P}\) is a set of all prime numbers.

A quick reminder of how modulo arithmetic works. Reading a clock is already a familiar concept, and uses hours that are \(\mod 12\). As soon as the hour hand goes past twelve, it wraps back around to one. An interesting property of this system is that by simply looking at a clock, we can’t deduce how many times the hour hand has wrapped around. That is to say, if 48 hours have passed, I wouldn’t know that just from learning that it is 1:30. However, if we know that the hour hand has passed twelve 4 times, we can fully solve for the number of hours that have passed with a simple formula, \(a = mq + r\), where \(m\) is our modulo divisor (here \(m = 12\)), \(q\) is our quotient (the number of times that our divisor evenly goes into the original number, here \(q = 4\)), and \(r\) is our remainder, which is usually what calling a modulo operator gives us (here \(r = 1.5\)). Knowing all of these values allows us to solve for \(a = 49.5\), but if we were missing the quotient, we would never be able to reconstruct our original value.

We can demonstrate how this helps the security of our scheme by applying it to our previous example and using \(p = 73\). Our new polynomial function is \(f’(x) = 42 + 3x + 5x^2 \mod 73\), and our new points are \((18, 37), (27, 45), (31, 49), (35, 67)\). Our entrusted individuals can once again use polynomial interpolation to recover our function, only this time addition and multiplication operations must be followed by reduction modulo \(p\) (e.g. \(48 + 93 \mod 73 = 68\)).

Using this new example, let us suppose that our attacker has obtained knowledge of two of these new points, \((18, 37), (27, 45)\), and the public information \(k = 3, p = 73\). This time the attacker deduces the following functions based on all of the information they have, where \(\mathbb{N}\) is a set of all positive integers, and \(q_x\) represents the quotient of the modulo of \(f’(x)\).

\[ \begin{aligned} f’(x) &= S + a_1x + a_2x^2 \mod 73 \\ f’(x) &= S + a_1x + a_2x^2 - 73q_x : q_x \in \mathbb{N} \\ f’(18) \equiv 37 &= S + a_118 + a_218^2 - 73q_{18} \\ f’(27) \equiv 45 &= S + a_127 + a_227^2 - 73q_{27} \end{aligned} \]

Now our attacker again solves for \(a_1\) by evaluating \(f’(27) - f’(18)\):

\[ \begin{aligned} 45 - 37 &= (S - S) + (27a_1 - 18a_1) + (729a_2 - 324a_2) + (-73q_{27} - (-73q_{18})) \\ 8 &= 9a_1 + 405a_2 + 73(q_{18} - q_{27}) \\ -9a_1 &= -8 + 405a_2 + 73(q_{18} - q_{27}) \\ 9a_1 &= 8 - 405a_2 - 73(q_{18} - q_{27}) \\ a_1 &= \frac{8 - 405a_2 - 73(q_{18} - q_{27})}{9} \\ a_1 &= \frac{8}{9} - 45a_2 - \frac{73}{9} (q_{18} - q_{27}) \end{aligned} \]

From there our attacker again attempts to deduce \(S\), by replacing \(a_1\) in \(f’(18)\):

\[ \begin{aligned} 37 &= S + 18\left(\frac{8}{9} - 45a_2 - \frac{73}{9} (q_{18} - q_{27})\right) + a_218^2 - 73q_{18} \\ -S &= 18\left(\frac{8}{9} - 45a_2 - \frac{73}{9} (q_{18} - q_{27})\right) + a_218^2 - 73q_{18} - 37 \\ S &= -18\left(\frac{8}{9} - 45a_2 - \frac{73}{9} (q_{18} - q_{27})\right) - 324a_2 + 73q_{18} + 37 \\ S &= 486a_2 + 21 + 219q_{18} - 146q_{27} \end{aligned} \]

This time around, our attacker has a serious problem. They are missing the values of \(a_2\), \(q_{18}\), and \(q_{27}\). Since there are an infinite number of valid combinations for these variables, there is no additional information that can be gained.

## Security Considerations

Sharmir’s secret sharing scheme offers *information-theoretic security*, meaning that the math we explored has been proven to be unbreakable, even against an attacker with unlimited computing power. However, the scheme still does contain a couple of known issues.

For example, Shamir’s scheme does not produce *verifiable shares*, which means that individuals are free to submit fake shares and prevent the correct secret from being reconstructed. An adversarial share holder with enough information can even produce a different share such that \(S\) is reconstructed to a value of their choice. This issue is addressed by *verifiable secret sharing schemes* such as Feldman’s scheme.

Another issue is that because the length of any given share is equal to the length of an associated secret, the length of a secret is easily leaked. This issue is trivial to fix by simply *padding* the secret to a fixed length.

Finally, it is important to note that our concerns about security may extend beyond just the scheme itself. For real world cryptography applications, there is often the threat of side channel attacks, in which an attacker attempts to extract useful information from application timing, caching, fault, and more. If this is a concern, careful considerations should be made during development, such as using constant time functions and lookups, preventing memory from paging to disk, and a bunch of other things that are beyond the scope of this blog post.

## Demo

The following demo is powered by the ssss-js library, which itself is a JavaScript port of the popular ssss program. Note that large values of \(k\), \(n\), and \(S\) may take a while to compute.