/*
* Copyright 2013 bits of proof zrt.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.bitsofproof.supernode.wallet;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import org.bouncycastle.util.Arrays;
import com.bitsofproof.supernode.common.ValidationException;
public class ShamirsSecretSharing
{
public static class SecretShare
{
public byte x;
public byte dl;
public byte yl;
public byte[] y;
}
private final static SecureRandom random = new SecureRandom ();
public static List<String> issueMnemonicShares (byte[] data, int pieces, int needed, String passphrase) throws ValidationException
{
List<String> m = new ArrayList<> ();
for ( SecretShare share : cut (data, pieces, needed) )
{
int l = share.y.length + 3;
if ( l % 8 != 0 )
{
l += 8 - l % 8;
}
byte[] e = new byte[l];
e[0] = share.x;
e[1] = share.dl;
e[2] = (byte) share.y.length;
System.arraycopy (share.y, 0, e, 3, share.y.length);
m.add (BIP39.encode (e, passphrase));
}
return m;
}
public static byte[] reconstructFromMnemonicShares (List<String> shareList, String passphrase) throws ValidationException
{
SecretShare[] shares = new SecretShare[shareList.size ()];
int i = 0;
for ( String m : shareList )
{
byte[] e = BIP39.decode (m, passphrase);
shares[i] = new SecretShare ();
shares[i].x = e[0];
shares[i].dl = e[1];
shares[i].yl = e[2];
shares[i].y = Arrays.copyOfRange (e, 3, shares[i].yl + 3);
++i;
}
return reconstruct (shares);
}
public static SecretShare[] cut (byte[] secret, int pieces, int needed) throws ValidationException
{
if ( secret.length > 127 )
{
throw new ValidationException ("secret too long");
}
BigInteger[] coeff = new BigInteger[needed - 1];
for ( int i = 0; i < coeff.length; ++i )
{
byte[] r = new byte[secret.length];
random.nextBytes (r);
coeff[i] = new BigInteger (r);
}
SecretShare[] shares = new SecretShare[pieces];
for ( int x = 1; x <= pieces; ++x )
{
int pow = x;
BigInteger poly = new BigInteger (1, secret);
for ( int i = 0; i < needed - 1; ++i )
{
poly = poly.add (BigInteger.valueOf (pow).multiply (coeff[i]));
pow *= x;
}
shares[x - 1] = new SecretShare ();
shares[x - 1].x = (byte) x;
shares[x - 1].dl = (byte) secret.length;
shares[x - 1].y = poly.toByteArray ();
shares[x - 1].yl = (byte) poly.toByteArray ().length;
}
return shares;
}
public static byte[] reconstruct (SecretShare[] shares) throws ValidationException
{
for ( int i = 0; i < shares.length - 1; ++i )
{
for ( int j = 0; j < shares.length; ++j )
{
if ( i != j && shares[i].x == shares[j].x )
{
throw new ValidationException ("Shares are not unique");
}
}
}
BigInteger[] y = new BigInteger[shares.length];
for ( int i = 0; i < shares.length; ++i )
{
y[i] = new BigInteger (shares[i].y);
}
int d, i;
for ( d = 1; d < shares.length; d++ )
{
for ( i = 0; i < shares.length - d; i++ )
{
int j = i + d;
BigInteger num =
BigInteger.valueOf (shares[j].x).multiply (y[i]).subtract (BigInteger.valueOf (shares[i].x).multiply (y[i + 1]));
BigInteger den = BigInteger.valueOf (shares[j].x).subtract (BigInteger.valueOf (shares[i].x));
y[i] = num.divide (den);
}
}
byte[] result = y[0].toByteArray ();
if ( result.length > shares[0].dl )
{
result = Arrays.copyOfRange (result, result.length - shares[0].dl, result.length);
}
if ( result.length < shares[0].dl )
{
byte[] tmp = new byte[shares[0].dl];
System.arraycopy (result, 0, tmp, shares[0].dl - result.length, result.length);
result = tmp;
}
return result;
}
}