github.com/cloudflare/circl@v1.5.0/tss/rsa/keyshare.go (about)

     1  package rsa
     2  
     3  import (
     4  	"crypto/rand"
     5  	"crypto/rsa"
     6  	"encoding/binary"
     7  	"errors"
     8  	"fmt"
     9  	"io"
    10  	"math"
    11  	"math/big"
    12  	"sync"
    13  )
    14  
    15  // KeyShare represents a portion of the key. It can only be used to generate SignShare's. During the dealing phase (when Deal is called), one KeyShare is generated per player.
    16  type KeyShare struct {
    17  	si *big.Int
    18  
    19  	twoDeltaSi *big.Int // optional cached value, this value is used to marginally speed up SignShare generation in Sign. If nil, it will be generated when needed and then cached.
    20  	Index      uint     // When KeyShare's are generated they are each assigned an index sequentially
    21  
    22  	Players   uint
    23  	Threshold uint
    24  }
    25  
    26  func (kshare KeyShare) String() string {
    27  	return fmt.Sprintf("(t,n): (%v,%v) index: %v si: 0x%v",
    28  		kshare.Threshold, kshare.Players, kshare.Index, kshare.si.Text(16))
    29  }
    30  
    31  // MarshalBinary encodes a KeyShare into a byte array in a format readable by UnmarshalBinary.
    32  // Note: Only Index's up to math.MaxUint16 are supported
    33  func (kshare *KeyShare) MarshalBinary() ([]byte, error) {
    34  	// The encoding format is
    35  	// | Players: uint16 | Threshold: uint16 | Index: uint16 | siLen: uint16 | si: []byte | twoDeltaSiNil: bool | twoDeltaSiLen: uint16 | twoDeltaSi: []byte |
    36  	// with all values in big-endian.
    37  
    38  	if kshare.Players > math.MaxUint16 {
    39  		return nil, fmt.Errorf("rsa_threshold: keyshare marshall: Players is too big to fit in a uint16")
    40  	}
    41  
    42  	if kshare.Threshold > math.MaxUint16 {
    43  		return nil, fmt.Errorf("rsa_threshold: keyshare marshall: Threshold is too big to fit in a uint16")
    44  	}
    45  
    46  	if kshare.Index > math.MaxUint16 {
    47  		return nil, fmt.Errorf("rsa_threshold: keyshare marshall: Index is too big to fit in a uint16")
    48  	}
    49  
    50  	players := uint16(kshare.Players)
    51  	threshold := uint16(kshare.Threshold)
    52  	index := uint16(kshare.Index)
    53  
    54  	twoDeltaSiBytes := []byte(nil)
    55  	if kshare.twoDeltaSi != nil {
    56  		twoDeltaSiBytes = kshare.twoDeltaSi.Bytes()
    57  	}
    58  
    59  	twoDeltaSiLen := len(twoDeltaSiBytes)
    60  
    61  	if twoDeltaSiLen > math.MaxInt16 {
    62  		return nil, fmt.Errorf("rsa_threshold: keyshare marshall: twoDeltaSiBytes is too big to fit it's length in a uint16")
    63  	}
    64  
    65  	siBytes := kshare.si.Bytes()
    66  
    67  	siLength := len(siBytes)
    68  
    69  	if siLength == 0 {
    70  		siLength = 1
    71  		siBytes = []byte{0}
    72  	}
    73  
    74  	if siLength > math.MaxInt16 {
    75  		return nil, fmt.Errorf("rsa_threshold: keyshare marshall: siBytes is too big to fit it's length in a uint16")
    76  	}
    77  
    78  	blen := 2 + 2 + 2 + 2 + 2 + 1 + siLength + twoDeltaSiLen
    79  	out := make([]byte, blen)
    80  
    81  	binary.BigEndian.PutUint16(out[0:2], players)
    82  	binary.BigEndian.PutUint16(out[2:4], threshold)
    83  	binary.BigEndian.PutUint16(out[4:6], index)
    84  
    85  	binary.BigEndian.PutUint16(out[6:8], uint16(siLength)) // okay because of conditions checked above
    86  
    87  	copy(out[8:8+siLength], siBytes)
    88  
    89  	if twoDeltaSiBytes != nil {
    90  		out[8+siLength] = 1 // twoDeltaSiNil
    91  	}
    92  
    93  	binary.BigEndian.PutUint16(out[8+siLength+1:8+siLength+3], uint16(twoDeltaSiLen))
    94  
    95  	if twoDeltaSiBytes != nil {
    96  		copy(out[8+siLength+3:8+siLength+3+twoDeltaSiLen], twoDeltaSiBytes)
    97  	}
    98  
    99  	return out, nil
   100  }
   101  
   102  // UnmarshalBinary recovers a KeyShare from a slice of bytes, or returns an error if the encoding is invalid.
   103  func (kshare *KeyShare) UnmarshalBinary(data []byte) error {
   104  	// The encoding format is
   105  	// | Players: uint16 | Threshold: uint16 | Index: uint16 | siLen: uint16 | si: []byte | twoDeltaSiNil: bool | twoDeltaSiLen: uint16 | twoDeltaSi: []byte |
   106  	// with all values in big-endian.
   107  	if len(data) < 6 {
   108  		return fmt.Errorf("rsa_threshold: keyshare unmarshalKeyShareTest failed: data length was too short for reading Players, Threshold, Index")
   109  	}
   110  
   111  	players := binary.BigEndian.Uint16(data[0:2])
   112  	threshold := binary.BigEndian.Uint16(data[2:4])
   113  	index := binary.BigEndian.Uint16(data[4:6])
   114  
   115  	if len(data[6:]) < 2 {
   116  		return fmt.Errorf("rsa_threshold: keyshare unmarshalKeyShareTest failed: data length was too short for reading siLen length")
   117  	}
   118  
   119  	siLen := binary.BigEndian.Uint16(data[6:8])
   120  
   121  	if siLen == 0 {
   122  		return fmt.Errorf("rsa_threshold: keyshare unmarshalKeyShareTest failed: si is a required field but siLen was 0")
   123  	}
   124  
   125  	if uint16(len(data[8:])) < siLen {
   126  		return fmt.Errorf("rsa_threshold: keyshare unmarshalKeyShareTest failed: data length was too short for reading si, needed: %d found: %d", siLen, len(data[8:]))
   127  	}
   128  
   129  	si := new(big.Int).SetBytes(data[8 : 8+siLen])
   130  
   131  	if len(data[8+siLen:]) < 1 {
   132  		return fmt.Errorf("rsa_threshold: keyshare unmarshalKeyShareTest failed: data length was too short for reading twoDeltaSiNil")
   133  	}
   134  
   135  	isNil := data[8+siLen]
   136  
   137  	var twoDeltaSi *big.Int
   138  
   139  	if isNil != 0 {
   140  		if len(data[8+siLen+1:]) < 2 {
   141  			return fmt.Errorf("rsa_threshold: keyshare unmarshalKeyShareTest failed: data length was too short for reading twoDeltaSiLen length")
   142  		}
   143  
   144  		twoDeltaSiLen := binary.BigEndian.Uint16(data[8+siLen+1 : 8+siLen+3])
   145  
   146  		if uint16(len(data[8+siLen+3:])) < twoDeltaSiLen {
   147  			return fmt.Errorf("rsa_threshold: keyshare unmarshalKeyShareTest failed: data length was too short for reading twoDeltaSi, needed: %d found: %d", twoDeltaSiLen, len(data[8+siLen+2:]))
   148  		}
   149  
   150  		twoDeltaSi = new(big.Int).SetBytes(data[8+siLen+3 : 8+siLen+3+twoDeltaSiLen])
   151  	}
   152  
   153  	kshare.Players = uint(players)
   154  	kshare.Threshold = uint(threshold)
   155  	kshare.Index = uint(index)
   156  	kshare.si = si
   157  	kshare.twoDeltaSi = twoDeltaSi
   158  
   159  	return nil
   160  }
   161  
   162  // Returns the cached value in twoDeltaSi or if nil, generates 2∆s_i, stores it in twoDeltaSi, and returns it
   163  func (kshare *KeyShare) get2DeltaSi(players int64) *big.Int {
   164  	// use the cached value if it exists
   165  	if kshare.twoDeltaSi != nil {
   166  		return kshare.twoDeltaSi
   167  	}
   168  	delta := calculateDelta(players)
   169  	// 2∆s_i
   170  	// delta << 1 == delta * 2
   171  	delta.Lsh(delta, 1).Mul(delta, kshare.si)
   172  	kshare.twoDeltaSi = delta
   173  	return delta
   174  }
   175  
   176  // Sign msg using a KeyShare. msg MUST be padded and hashed. Call PadHash before this method.
   177  //
   178  // If rand is not nil then blinding will be used to avoid timing
   179  // side-channel attacks.
   180  //
   181  // parallel indicates whether the blinding operations should use go routines to operate in parallel.
   182  // If parallel is false, blinding will take about 2x longer than nonbinding, otherwise it will take about the same time
   183  // (see benchmarks). If randSource is nil, parallel has no effect. parallel should almost always be set to true.
   184  func (kshare *KeyShare) Sign(randSource io.Reader, pub *rsa.PublicKey, digest []byte, parallel bool) (SignShare, error) {
   185  	x := &big.Int{}
   186  	x.SetBytes(digest)
   187  
   188  	exp := kshare.get2DeltaSi(int64(kshare.Players))
   189  
   190  	var signShare SignShare
   191  	signShare.Players = kshare.Players
   192  	signShare.Threshold = kshare.Threshold
   193  	signShare.Index = kshare.Index
   194  
   195  	signShare.xi = &big.Int{}
   196  
   197  	if randSource != nil {
   198  		// Let's blind.
   199  		// We can't use traditional RSA blinding (as used in rsa.go) because we are exponentiating by exp and not d.
   200  		// As such, Euler's theorem doesn't apply ( exp * d != 0 (mod ϕ(n)) ).
   201  		// Instead, we will choose a random r and compute x^{exp+r} * x^{-r} = x^{exp}.
   202  		// This should (hopefully) prevent revealing information of the true value of exp, since with exp you can derive
   203  		// s_i, the secret key share.
   204  
   205  		r, err := rand.Int(randSource, pub.N)
   206  		if err != nil {
   207  			return SignShare{}, errors.New("rsa_threshold: unable to get random value for blinding")
   208  		}
   209  		expPlusr := big.Int{}
   210  		// exp + r
   211  		expPlusr.Add(exp, r)
   212  
   213  		var wg *sync.WaitGroup
   214  
   215  		// x^{|2∆s_i+r|}
   216  		if parallel {
   217  			wg = &sync.WaitGroup{}
   218  			wg.Add(1)
   219  			go func() {
   220  				signShare.xi.Exp(x, &expPlusr, pub.N)
   221  				wg.Done()
   222  			}()
   223  		} else {
   224  			signShare.xi.Exp(x, &expPlusr, pub.N)
   225  		}
   226  
   227  		xExpr := big.Int{}
   228  		// x^r
   229  		xExpr.Exp(x, r, pub.N)
   230  		// x^{-r}
   231  		res := xExpr.ModInverse(&xExpr, pub.N)
   232  
   233  		if res == nil {
   234  			// extremely unlikely, somehow x^r is p or q
   235  			return SignShare{}, errors.New("rsa_threshold: no mod inverse")
   236  		}
   237  
   238  		if wg != nil {
   239  			wg.Wait()
   240  		}
   241  
   242  		// x^{|2∆s_i+r|} * x^{-r} = x^{2∆s_i}
   243  		signShare.xi.Mul(signShare.xi, &xExpr)
   244  		signShare.xi.Mod(signShare.xi, pub.N)
   245  	} else {
   246  		// x^{2∆s_i}
   247  		signShare.xi = &big.Int{}
   248  		signShare.xi.Exp(x, exp, pub.N)
   249  	}
   250  
   251  	return signShare, nil
   252  }