├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── key.go ├── key_meta.go ├── key_share.go ├── key_test.go ├── pkcs1_encoding.go ├── pkcs1_encoding_test.go ├── polynomial.go ├── polynomial_test.go ├── signature_share.go ├── signature_share_list.go ├── signature_share_list_test.go ├── tcrsa_test.go ├── utils.go ├── utils_test.go └── verification_key.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .vscode 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.x 5 | - 1.11.x 6 | - master -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | LICENSE 2 | 3 | Copyright (c) 2020, NIC Chile Research Labs (NIC Labs), Universidad de Chile. 4 | All rights reserved. 5 | 6 | 1.- The process of signing documents using threshold cryptography is protected 7 | under the patent "System and method for secure electronic communications 8 | through hardware-based security threshold cryptography" 10735188 (USA, 2020) 9 | and 2015003766 (Chile, 2015). For any commercial use of such technology please 10 | refer to the Vice Presidency of Research and Development of Universidad de 11 | Chile at https://uchile.cl/u5077 12 | 13 | 2.- NIC Labs has a rich history of collaboration with the Open Source Community 14 | and the development of the DNS infrastructure. Therefore, we provide the source 15 | code of our implementation of the distributed Hardware Security Module (dHSM), 16 | formerly Threshold Cryptography Hardware Security Module, aiming to increase the 17 | alternatives to DNS admins for DNSSEC procedures. 18 | 19 | 3.- Also, knowing that some parts of our code are not related particularly to 20 | the procedure claimed in 1, we decide to extend the permission granted in 2. 21 | Permission is hereby granted, free of charge, to any person or organization 22 | obtaining a copy of the software and accompanying documentation covered by this 23 | license (the "Software") to use, reproduce, display, distribute, execute, and 24 | transmit the Software, parts of the Software, and to prepare derivative works of 25 | the Software, and to permit third-parties to whom the Software is furnished to 26 | do so, all subject to the following: 27 | 28 | 3.1.- The use of the Software (or the parts of this Software used in derivative 29 | works) does not infringe the patent signaled in 1. 30 | 31 | 3.2.- The use of the Software (or the parts of this Software used in derivative 32 | works) is used on any Open Source Software Implementation. Where “Open Source 33 | Software” means software whose source code is published and made available for 34 | inspection and use by anyone because either (a) the source code is subject to a 35 | license that permits recipients to copy, modify, and distribute the source code 36 | without payment of fees or royalties, or (b) the source code is in the public 37 | domain, including code released for public use through a CC0 waiver. All 38 | licenses certified by the Open Source Initiative at opensource.org as of 39 | January 5, 2021 and all Creative Commons licenses identified on the 40 | creativecommons.org website as of January 5, 2021, including the Public License 41 | Fallback of the CC0 waiver, satisfy these requirements for the purposes of this 42 | license. 43 | 44 | 3.3.- If you or your affiliates institute patent litigation (including, but not 45 | limited to, a cross-claim or counterclaim in a lawsuit) against any entity 46 | alleging that any use authorized by this license infringes another patent, then 47 | any rights granted to you under this license automatically terminate as of the 48 | date such litigation is filed. 49 | 50 | 3.4.- The copyright notices in the Software and this entire statement, including 51 | the above license grant, this restriction and the following disclaimer, must be 52 | included in all copies of the Software, in whole or in part, and all derivative 53 | works of the Software, unless such copies or derivative works are solely in the 54 | form of machine-executable object code generated by a source language processor. 55 | 56 | 4.- DISCLAIMER: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 57 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 58 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. 59 | IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE 60 | LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR 61 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 62 | OR OTHER DEALINGS IN THE SOFTWARE. 63 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Golang Threshold Cryptography Library - RSA implementation 2 | [![Go Report Card](https://goreportcard.com/badge/github.com/niclabs/tcrsa)](https://goreportcard.com/report/github.com/niclabs/tcrsa) [![Build Status](https://travis-ci.org/niclabs/tcrsa.svg?branch=master)](https://travis-ci.org/niclabs/tcrsa) [![GoDoc](https://godoc.org/github.com/niclabs/libtc-rsa?status.svg)](https://godoc.org/github.com/niclabs/tcrsa) 3 | 4 | This library implements the cryptographic algorithms of Victor Shoup's paper [Practical Threshold Signatures](http://www.iacr.org/archive/eurocrypt2000/1807/18070209-new.pdf) in the Golang programming language. 5 | 6 | The codebase, commments and optimizations were ported from a previous implementation in C language, called [tchsm-libtc](https://github.com/niclabs/tchsm-libtc). As the previous implementation, the objective of this library is to provide a set of primitives to work with. 7 | 8 | ### Requirements 9 | 10 | Due to Golang extensive standard library, this implementation does not have external requirements (obviously aside of [Golang](https://golang.org), version 1.12 or above). 11 | 12 | ### Installing 13 | 14 | ```shell 15 | go get https://github.com/niclabs/tcrsa 16 | ``` 17 | 18 | To run the tests you just need to use `go test`: 19 | 20 | ```shell 21 | go test 22 | ``` -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/niclabs/tcrsa 2 | 3 | go 1.12 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/niclabs/libtc-rsa v0.0.0-20190415152758-2a15e45a7d0e h1:7lP0VhVtU+wzZVMiZkW5tdoPYrxP9HQETqWP0toMnnI= 2 | -------------------------------------------------------------------------------- /key.go: -------------------------------------------------------------------------------- 1 | // Package tcrsa implements the cryptographic algorithms of Victor Shoup's paper Practical Threshold Signatures, in the Golang programming language. 2 | // You can find the paper defining the algorithms in http://www.iacr.org/archive/eurocrypt2000/1807/18070209-new.pdf. 3 | package tcrsa 4 | 5 | import ( 6 | "crypto/rand" 7 | "crypto/rsa" 8 | "fmt" 9 | "math/big" 10 | ) 11 | 12 | // Minimum bit size for the key generation: 512 bits. 13 | const minBitSize = 1 << 9 14 | 15 | // Maximum bit size for the key generation: 4096 bits. 16 | const maxBitSize = 1 << 13 17 | 18 | // Fermat fourth number 19 | // Default e value. 20 | const f4 = 65537 21 | 22 | // NewKey creates l key shares for a k-threshold signing scheme. 23 | // The bit_size parameter is used to generate key shares with a security level equivalent to a RSA private of that size. 24 | // The generated key shares have a threshold parameter of k. This means that k valid signatures are needed to sign. 25 | // On success, it returns the meta information common to all the keys, and an array with all the key shares. 26 | // On failure, it returns an error and invalid pointers to shares and meta information. 27 | func NewKey(bitSize int, k, l uint16, args *KeyMetaArgs) (shares KeyShareList, meta *KeyMeta, err error) { 28 | 29 | if args == nil { 30 | args = &KeyMetaArgs{} 31 | } 32 | 33 | // Parameter checking 34 | if bitSize < minBitSize || bitSize > maxBitSize { 35 | err = fmt.Errorf("bit size should be between %d and %d, but it is %d", minBitSize, maxBitSize, bitSize) 36 | return 37 | } 38 | if l <= 1 { 39 | err = fmt.Errorf("l should be greater than 1, but it is %d", l) 40 | return 41 | } 42 | if k <= 0 { 43 | err = fmt.Errorf("k should be greater than 0, but it is %d", k) 44 | return 45 | } 46 | if k < (l/2+1) || k > l { 47 | err = fmt.Errorf("k should be between the %d and %d, but it is %d", (l/2)+1, l, k) 48 | return 49 | } 50 | 51 | pPrimeSize := (bitSize + 1) / 2 52 | qPrimeSize := bitSize - pPrimeSize - 1 53 | 54 | if args.P != nil && args.P.BitLen() != pPrimeSize { 55 | err = fmt.Errorf("P bit length is %d, but it should be %d", args.P.BitLen(), pPrimeSize) 56 | return 57 | } 58 | if args.Q != nil && args.Q.BitLen() != qPrimeSize { 59 | err = fmt.Errorf("Q bit length is %d, but it should be %d", args.Q.BitLen(), qPrimeSize) 60 | return 61 | } 62 | 63 | meta = &KeyMeta{ 64 | PublicKey: &rsa.PublicKey{}, 65 | K: k, 66 | L: l, 67 | VerificationKey: NewVerificationKey(l), 68 | } 69 | shares = make(KeyShareList, meta.L) 70 | 71 | var i uint16 72 | for i = 0; i < meta.L; i++ { 73 | shares[i] = &KeyShare{} 74 | } 75 | 76 | // Init big numbers 77 | pr := new(big.Int) 78 | qr := new(big.Int) 79 | p := new(big.Int) 80 | q := new(big.Int) 81 | d := new(big.Int) 82 | e := new(big.Int) 83 | lBig := new(big.Int) 84 | m := new(big.Int) 85 | n := new(big.Int) 86 | deltaInv := new(big.Int) 87 | divisor := new(big.Int) 88 | r := new(big.Int) 89 | vkv := new(big.Int) 90 | vku := new(big.Int) 91 | vki := new(big.Int) 92 | 93 | if args.P != nil { 94 | if !args.P.ProbablyPrime(c) { 95 | err = fmt.Errorf("p should be prime, but it's not") 96 | return 97 | } 98 | p.Set(args.P) 99 | pr.Sub(p, big.NewInt(1)).Div(pr, big.NewInt(2)) 100 | } else { 101 | if p, pr, err = generateSafePrimes(pPrimeSize, rand.Reader); err != nil { 102 | return 103 | } 104 | } 105 | 106 | if args.Q != nil { 107 | if !args.Q.ProbablyPrime(c) { 108 | err = fmt.Errorf("q should be prime, but it's not") 109 | return 110 | } 111 | q.Set(args.Q) 112 | qr.Sub(q, big.NewInt(1)).Div(qr, big.NewInt(2)) 113 | } else { 114 | if q, qr, err = generateSafePrimes(qPrimeSize, rand.Reader); err != nil { 115 | return 116 | } 117 | } 118 | 119 | // n = p * q and m = p' * q' 120 | n.Mul(p, q) 121 | m.Mul(pr, qr) 122 | 123 | meta.PublicKey.N = n 124 | 125 | lBig.SetUint64(uint64(l)) 126 | 127 | eSet := false 128 | 129 | if args.E != 0 { 130 | meta.PublicKey.E = args.E 131 | e = big.NewInt(int64(meta.PublicKey.E)) 132 | if e.ProbablyPrime(c) && lBig.Cmp(e) < 0 { 133 | eSet = true 134 | } 135 | } 136 | if !eSet { 137 | meta.PublicKey.E = f4 138 | e = big.NewInt(int64(meta.PublicKey.E)) 139 | } 140 | 141 | // d = e^{-1} mod m 142 | d.ModInverse(e, m) 143 | 144 | // generate v 145 | if args.R == nil { 146 | for divisor.Cmp(big.NewInt(1)) != 0 { 147 | r, err = randInt(n.BitLen()) 148 | if err != nil { 149 | return 150 | } 151 | divisor.GCD(nil, nil, r, n) 152 | } 153 | } else { 154 | divisor.GCD(nil, nil, args.R, n) 155 | if divisor.Cmp(big.NewInt(1)) != 0 { 156 | err = fmt.Errorf("provided r value should be coprime with p*q (i.e., it should not be 0, 1, p or q)") 157 | return 158 | } 159 | r.Set(args.R) 160 | } 161 | 162 | vkv.Exp(r, big.NewInt(2), n) 163 | 164 | meta.VerificationKey.V = vkv.Bytes() 165 | 166 | // generate u 167 | if args.U == nil { 168 | for cond := true; cond; cond = big.Jacobi(vku, n) != -1 { 169 | vku, err = randInt(n.BitLen()) 170 | if err != nil { 171 | return 172 | } 173 | vku.Mod(vku, n) 174 | } 175 | } else { 176 | vku.Set(args.U) 177 | } 178 | 179 | meta.VerificationKey.U = vku.Bytes() 180 | 181 | // Delta is fact(l) 182 | deltaInv.MulRange(1, int64(l)).ModInverse(deltaInv, m) 183 | 184 | // Generate polynomial with random coefficients. 185 | var poly polynomial 186 | poly, err = createRandomPolynomial(int(k-1), d, m) 187 | 188 | if err != nil { 189 | return 190 | } 191 | 192 | // Calculate Key Shares for each i TC participant. 193 | for i = 1; i <= meta.L; i++ { 194 | keyShare := shares[i-1] 195 | keyShare.Id = i 196 | si := poly.eval(big.NewInt(int64(i))) 197 | si.Mul(si, deltaInv) 198 | si.Mod(si, m) 199 | keyShare.Si = si.Bytes() 200 | vki.Exp(vkv, si, n) 201 | 202 | meta.VerificationKey.I[i-1] = vki.Bytes() 203 | } 204 | return 205 | } 206 | -------------------------------------------------------------------------------- /key_meta.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "crypto/rsa" 5 | "math/big" 6 | ) 7 | 8 | // KeyMeta stores the meta information of a distributed key generation. 9 | // It stores the RSA public key, the threshold value k and the total shares value l. 10 | // It also has stored the verification keys for each signed share. 11 | type KeyMeta struct { 12 | PublicKey *rsa.PublicKey // RSA Public key used to verify signatures 13 | K uint16 // Threshold 14 | L uint16 // Total number of participants 15 | VerificationKey *VerificationKey // Verification Key associated to a Key Generation. 16 | } 17 | 18 | // KeyMetaArgs defines the initialization values for key generation. 19 | // It allows to load previously computed keys. Useful for testing. Completely forbidden for 20 | // production use. 21 | type KeyMetaArgs struct { 22 | E int // Public exponent. This value should be prime. 23 | P *big.Int // A prime, it should have the half of the bitsize. 24 | Q *big.Int // Another prime, it should have the other half of the bitsize. 25 | R *big.Int // A random prime but it must be coprime with P*Q. 26 | U *big.Int // An arbitrary random value. 27 | } 28 | -------------------------------------------------------------------------------- /key_share.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "bytes" 5 | "crypto" 6 | "crypto/sha256" 7 | "encoding/base64" 8 | "math/big" 9 | ) 10 | 11 | // KeyShare stores the Si value of a node and an unique incremental ID for the node. 12 | // It's used to generate a signature share. 13 | type KeyShare struct { 14 | Si []byte // S_i value of the Key Share. 15 | Id uint16 // ID of the key share. 16 | } 17 | 18 | // KeyShareList is a list of KeyShare values. 19 | type KeyShareList []*KeyShare 20 | 21 | // EqualsSi compares two key share S_i values and returns true if they are equal. 22 | func (keyShare KeyShare) EqualsSi(keyShare2 *KeyShare) bool { 23 | if keyShare2 == nil { 24 | return false 25 | } 26 | return bytes.Compare(keyShare.Si, keyShare2.Si) == 0 27 | } 28 | 29 | // ToBase64 transforms a Si value of a keyshare to Base64, and returns it. 30 | func (keyShare KeyShare) ToBase64() string { 31 | return base64.StdEncoding.EncodeToString(keyShare.Si) 32 | } 33 | 34 | // Sign generates a signature share using a key share. A standard RSA signature is generated using several 35 | // signature shares. The document to be signed should be prepared (hashed and padded) before using this function. 36 | // It returns a SigShare with the signature of this node, or an error if the signing process failed. 37 | func (keyShare KeyShare) Sign(doc []byte, hashType crypto.Hash, info *KeyMeta) (sigShare *SigShare, err error) { 38 | 39 | x := new(big.Int) 40 | xi := new(big.Int) 41 | z := new(big.Int) 42 | c := new(big.Int) 43 | n := new(big.Int) 44 | e := new(big.Int) 45 | v := new(big.Int) 46 | u := new(big.Int) 47 | vki := new(big.Int) 48 | xTilde := new(big.Int) 49 | xi2 := new(big.Int) 50 | vPrime := new(big.Int) 51 | xPrime := new(big.Int) 52 | exp := new(big.Int) 53 | si := new(big.Int) 54 | 55 | x.SetBytes(doc) 56 | n.Set(info.PublicKey.N) 57 | e.SetUint64(uint64(info.PublicKey.E)) 58 | v.SetBytes(info.VerificationKey.V) 59 | u.SetBytes(info.VerificationKey.U) 60 | vki.SetBytes(info.VerificationKey.I[keyShare.Id-1]) 61 | 62 | si.SetBytes(keyShare.Si) 63 | 64 | // x = doc if (doc | n) == 1 else doc * u^e 65 | if big.Jacobi(x, n) == -1 { 66 | ue := new(big.Int).Exp(u, e, n) 67 | x.Mul(x, ue).Mod(x, n) 68 | } 69 | // xi = x^(2*keyShare) mod n 70 | exp.Mul(si, big.NewInt(2)) 71 | xi.Exp(x, exp, n) 72 | // x~ = x^4 % n 73 | xTilde.Exp(x, big.NewInt(4), n) 74 | 75 | // xi2 = xi^2 % n 76 | xi2.Exp(xi, big.NewInt(2), n) 77 | 78 | // r = abs(random(bytes_len)) 79 | r, err := randInt(n.BitLen() + 2*hashType.Size()*8) 80 | if err != nil { 81 | return 82 | } 83 | 84 | // v' = v^r % n 85 | vPrime.Exp(v, r, n) 86 | 87 | // x' = x~^r % n 88 | xPrime.Exp(xTilde, r, n) 89 | 90 | // Hashing all the values 91 | sha := sha256.New() 92 | sha.Write(v.Bytes()) 93 | sha.Write(u.Bytes()) 94 | sha.Write(xTilde.Bytes()) 95 | sha.Write(vki.Bytes()) 96 | sha.Write(xi2.Bytes()) 97 | sha.Write(vPrime.Bytes()) 98 | sha.Write(xPrime.Bytes()) 99 | 100 | hash := sha.Sum(nil) 101 | 102 | c.SetBytes(hash) 103 | c.Mod(c, n) 104 | 105 | z.Mul(c, si) 106 | z.Add(z, r) 107 | 108 | sigShare = &SigShare{ 109 | Id: keyShare.Id, 110 | Xi: xi.Bytes(), 111 | C: c.Bytes(), 112 | Z: z.Bytes(), 113 | } 114 | return 115 | } 116 | -------------------------------------------------------------------------------- /key_test.go: -------------------------------------------------------------------------------- 1 | package tcrsa_test 2 | 3 | import ( 4 | "crypto" 5 | "crypto/rsa" 6 | "crypto/sha256" 7 | "encoding/base64" 8 | "fmt" 9 | "github.com/niclabs/tcrsa" 10 | "math/big" 11 | "testing" 12 | ) 13 | 14 | const keyTestK = 3 15 | const keyTestL = 5 16 | 17 | const keyTestFixedSize = 512 18 | const keyTestFixedP = "132TWiSEqNNnfiF5AZjS2R8SwUszMGnHSKTYAtWckuc=" 19 | const keyTestFixedQ = "f8PooDmAlOUFf3BdAxPCOy8p5ArfLHs6ODFWTFnpUxM=" 20 | const keyTestFixedR = "UfF0MWqXf+K4GjmcWhxdK3CH/XVsDxm8r+CqBenL7TfdWNAD4rpUMIHzhqb0WV6KAAJfGEBlHyj1JH2rr9LiUA==" 21 | const keyTestFixedU = "CpJe+VzsAI3FcPioeMXklkxFFb+M9MaN1VzuScOs+7bwvczarYABZhyjPFC8McXCFAJIvaKTZwTlpylwJPumZw==" 22 | 23 | const keyTestHashType = crypto.SHA256 24 | const keyTestSize = 512 25 | const keyTestMessage = "Hello world" 26 | 27 | func TestGenerateKeys_differentKeys(t *testing.T) { 28 | keyShares, _, err := tcrsa.NewKey(keyTestSize, keyTestK, keyTestL, &tcrsa.KeyMetaArgs{}) 29 | if err != nil { 30 | t.Errorf("couldn't create keys") 31 | } 32 | 33 | for i := 0; i < len(keyShares); i++ { 34 | key1 := keyShares[i] 35 | for j := i + 1; j < len(keyShares); j++ { 36 | key2 := keyShares[j] 37 | if key1.EqualsSi(key2) { 38 | t.Errorf("key shares are equal: k%d=%s, k%d=%s", i, key1.ToBase64(), j, key2.ToBase64()) 39 | } 40 | } 41 | } 42 | } 43 | 44 | func TestGenerateKeys_validRandom(t *testing.T) { 45 | k := uint16(keyTestK) 46 | l := uint16(keyTestL) 47 | 48 | keyMetaArgs := &tcrsa.KeyMetaArgs{} 49 | 50 | keyShares, keyMeta, err := tcrsa.NewKey(keyTestSize, uint16(k), uint16(l), keyMetaArgs) 51 | if err != nil { 52 | t.Errorf(fmt.Sprintf("%v", err)) 53 | } 54 | docHash := sha256.Sum256([]byte(keyTestMessage)) 55 | 56 | docPKCS1, err := tcrsa.PrepareDocumentHash(keyMeta.PublicKey.Size(), keyTestHashType, docHash[:]) 57 | if err != nil { 58 | t.Errorf(fmt.Sprintf("%v", err)) 59 | } 60 | 61 | sigShares := make(tcrsa.SigShareList, l) 62 | 63 | var i uint16 64 | for i = 0; i < l; i++ { 65 | sigShares[i], err = keyShares[i].Sign(docPKCS1, keyTestHashType, keyMeta) 66 | if err != nil { 67 | t.Errorf(fmt.Sprintf("%v", err)) 68 | } 69 | if err := sigShares[i].Verify(docPKCS1, keyMeta); err != nil { 70 | t.Errorf(fmt.Sprintf("%v", err)) 71 | } 72 | } 73 | signature, err := sigShares.Join(docPKCS1, keyMeta) 74 | if err != nil { 75 | t.Errorf(fmt.Sprintf("%v", err)) 76 | } 77 | 78 | if err := rsa.VerifyPKCS1v15(keyMeta.PublicKey, keyTestHashType, docHash[:], signature); err != nil { 79 | t.Errorf(fmt.Sprintf("%v", err)) 80 | } 81 | 82 | } 83 | 84 | func TestGenerateKeys_validFixed(t *testing.T) { 85 | 86 | k := uint16(keyTestK) 87 | l := uint16(keyTestL) 88 | keyMetaArgs := &tcrsa.KeyMetaArgs{} 89 | 90 | pBig, err := base64.StdEncoding.DecodeString(keyTestFixedP) 91 | if err != nil { 92 | t.Errorf("could not decode b64 key for p") 93 | } 94 | qBig, err := base64.StdEncoding.DecodeString(keyTestFixedQ) 95 | if err != nil { 96 | t.Errorf("could not decode b64 key for q") 97 | } 98 | rBig, err := base64.StdEncoding.DecodeString(keyTestFixedR) 99 | if err != nil { 100 | t.Errorf("could not decode b64 key for R") 101 | } 102 | vkuBig, err := base64.StdEncoding.DecodeString(keyTestFixedU) 103 | if err != nil { 104 | t.Errorf("could not decode b64 key for vk_u") 105 | } 106 | keyMetaArgs.P = new(big.Int).SetBytes(pBig) 107 | keyMetaArgs.Q = new(big.Int).SetBytes(qBig) 108 | keyMetaArgs.R = new(big.Int).SetBytes(rBig) 109 | keyMetaArgs.U = new(big.Int).SetBytes(vkuBig) 110 | 111 | keyShares, keyMeta, err := tcrsa.NewKey(keyTestFixedSize, uint16(k), uint16(l), keyMetaArgs) 112 | if err != nil { 113 | t.Errorf(fmt.Sprintf("%v", err)) 114 | } 115 | docHash := sha256.Sum256([]byte(keyTestMessage)) 116 | 117 | docPKCS1, err := tcrsa.PrepareDocumentHash(keyMeta.PublicKey.Size(), keyTestHashType, docHash[:]) 118 | if err != nil { 119 | t.Errorf(fmt.Sprintf("%v", err)) 120 | } 121 | 122 | sigShares := make(tcrsa.SigShareList, l) 123 | 124 | var i uint16 125 | for i = 0; i < l; i++ { 126 | sigShares[i], err = keyShares[i].Sign(docPKCS1, keyTestHashType, keyMeta) 127 | if err != nil { 128 | t.Errorf(fmt.Sprintf("%v", err)) 129 | } 130 | if err := sigShares[i].Verify(docPKCS1, keyMeta); err != nil { 131 | t.Errorf(fmt.Sprintf("%v", err)) 132 | } 133 | } 134 | signature, err := sigShares.Join(docPKCS1, keyMeta) 135 | 136 | if err != nil { 137 | t.Errorf(fmt.Sprintf("%v", err)) 138 | } 139 | 140 | sigB64 := base64.StdEncoding.EncodeToString(signature) 141 | 142 | if sigB64 != "BUNv4j1NkVFNwx6v0GVG6CfN1Y7yhOBG2Tyy7ci7VK+AVYukZdiajnaYPALHLsEwngDLgNPK40o6HhbWT+ikXQ==" { 143 | t.Errorf("signature is not as expected.") 144 | } 145 | 146 | if err := rsa.VerifyPKCS1v15(keyMeta.PublicKey, keyTestHashType, docHash[:], signature); err != nil { 147 | t.Errorf(fmt.Sprintf("%v", err)) 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /pkcs1_encoding.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "crypto" 5 | "errors" 6 | "fmt" 7 | ) 8 | 9 | // This section is copied almost literally from the golang crypto/rsa source code 10 | // https://golang.org/src/crypto/rsa/pkcs1v15.go 11 | // These are ASN1 DER structures: 12 | // DigestInfo ::= SEQUENCE { 13 | // digestAlgorithm AlgorithmIdentifier, 14 | // digest OCTET STRING 15 | // } 16 | // For performance, we don't use the generic ASN1 encoder. Rather, we 17 | // precompute a prefix of the digest value that makes a valid ASN1 DER string 18 | // with the correct contents. 19 | var hashPrefixes = map[crypto.Hash][]byte{ 20 | crypto.MD5: {0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, 0x00, 0x04, 0x10}, 21 | crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}, 22 | crypto.SHA224: {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c}, 23 | crypto.SHA256: {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20}, 24 | crypto.SHA384: {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30}, 25 | crypto.SHA512: {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40}, 26 | crypto.MD5SHA1: {}, // A special TLS case which doesn't use an ASN1 prefix. 27 | crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14}, 28 | } 29 | 30 | // Returns hash info needed to encode a string hash in PKCS v1.15 format. 31 | // This method was copied from SignPKCS15 function from crypto/rsa on https://golang.org/pkg/crypto/rsa/ 32 | func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) { 33 | // Special case: crypto.Hash(0) is used to indicate that the data is 34 | // signed directly. 35 | if hash == 0 { 36 | return inLen, nil, nil 37 | } 38 | hashLen = hash.Size() 39 | if inLen != hashLen { 40 | return 0, nil, errors.New("crypto/rsa: input must be hashed message") 41 | } 42 | prefix, ok := hashPrefixes[hash] 43 | if !ok { 44 | return 0, nil, errors.New("crypto/rsa: unsupported hash function") 45 | } 46 | return 47 | } 48 | 49 | // PrepareDocumentHash receives a document hash and encodes it in PKCS v1.15 for its signing. 50 | // This method was copied from SignPKCS15 function from crypto/rsa on https://golang.org/pkg/crypto/rsa/ 51 | func PrepareDocumentHash(privateKeySize int, hashType crypto.Hash, digest []byte) ([]byte, error) { 52 | hashLen, prefix, err := pkcs1v15HashInfo(hashType, len(digest)) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | tLen := len(prefix) + hashLen 58 | k := privateKeySize 59 | if k < tLen+11 { 60 | return nil, fmt.Errorf("message too long") 61 | } 62 | 63 | // EM = 0x00 || 0x01 || PS || 0x00 || T 64 | em := make([]byte, k) 65 | em[1] = 1 66 | for i := 2; i < k-tLen-1; i++ { 67 | em[i] = 0xff 68 | } 69 | copy(em[k-tLen:k-hashLen], prefix) 70 | copy(em[k-hashLen:k], digest) 71 | return em, nil 72 | } 73 | -------------------------------------------------------------------------------- /pkcs1_encoding_test.go: -------------------------------------------------------------------------------- 1 | package tcrsa_test 2 | 3 | import ( 4 | "crypto" 5 | "crypto/sha256" 6 | "github.com/niclabs/tcrsa" 7 | "testing" 8 | ) 9 | 10 | const pkcs1EncodingTestMessage = "Hello World" 11 | const pkcs1EncodingLeyLength = 64 12 | 13 | func TestPrepareDocumentHash(t *testing.T) { 14 | docHash := sha256.Sum256([]byte(pkcs1EncodingTestMessage)) 15 | docPKCS1, err := tcrsa.PrepareDocumentHash(pkcs1EncodingLeyLength, crypto.SHA256, docHash[:]) 16 | if err != nil { 17 | t.Errorf("couldn't prepare document hash: %v", err) 18 | } 19 | // Check byte length 20 | if len(docPKCS1) != pkcs1EncodingLeyLength { 21 | t.Errorf("prepared hash has not the desired length") 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /polynomial.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "strings" 7 | ) 8 | 9 | // polynomial represents a classic polynomial, with convenience methods useful for 10 | // the operations the Threshold Cryptography library needs. 11 | type polynomial []*big.Int 12 | 13 | // newPolynomial creates a polynomial of degree d with all its d+1 coefficients in 0. 14 | func newPolynomial(d int) polynomial { 15 | poly := make(polynomial, d+1) 16 | for i := 0; i < len(poly); i++ { 17 | poly[i] = new(big.Int) 18 | } 19 | return poly 20 | } 21 | 22 | // GetDegree returns the degree of a polynomial, which is the length of the coefficient 23 | // array, minus 1. 24 | func (p polynomial) getDegree() int { 25 | return len(p) - 1 26 | } 27 | 28 | // createRandomPolynomial creates a polynomial of degree "d" with random coefficients as terms 29 | // with degree greater than 1. The coefficient of the term of degree 0 is x0 and the module for all the 30 | // coefficients of the polynomial is m. 31 | func createRandomPolynomial(d int, x0, m *big.Int) (polynomial, error) { 32 | if m.Sign() < 0 { 33 | return polynomial{}, fmt.Errorf("m is negative") 34 | } 35 | bitLen := m.BitLen() - 1 36 | poly := newPolynomial(d) 37 | 38 | poly[0].Set(x0) 39 | 40 | for i := 1; i < len(poly); i++ { 41 | rand, err := randInt(bitLen) 42 | if err != nil { 43 | return polynomial{}, err 44 | } 45 | poly[i].Mod(rand, m) 46 | } 47 | return poly, nil 48 | } 49 | 50 | // createFixedPolynomial a polynomial of degree "d" with fixed coefficients for terms with 51 | // degree greater than 1. The coefficient of the term of degree 0 is x0 and the module of the 52 | // coefficients for the polynomial is m. 53 | func createFixedPolynomial(d int, x0, m *big.Int) (polynomial, error) { 54 | if m.Sign() < 0 { 55 | return polynomial{}, fmt.Errorf("m is negative") 56 | } 57 | poly := newPolynomial(d) 58 | poly[0].Set(x0) 59 | 60 | for i := 1; i < len(poly); i++ { 61 | rand := big.NewInt(int64(i)) 62 | poly[i].Mod(rand, m) 63 | } 64 | return poly, nil 65 | } 66 | 67 | // eval evaluates a polynomial to x with Horner's method and returns the result. 68 | func (p polynomial) eval(x *big.Int) *big.Int { 69 | y := big.NewInt(0) 70 | for k := len(p) - 1; k >= 0; k-- { 71 | y.Mul(y, x) 72 | y.Add(y, p[k]) 73 | } 74 | return y 75 | } 76 | 77 | // string returns the polynomial formatted as a string. 78 | func (p polynomial) String() string { 79 | s := make([]string, len(p)) 80 | for i := 0; i < len(p); i++ { 81 | s[i] = fmt.Sprintf("%dx^%d", p[i], i) 82 | } 83 | return strings.Join(s, " + ") 84 | } 85 | -------------------------------------------------------------------------------- /polynomial_test.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | ) 7 | 8 | const polynomialTestDegree = 3 9 | 10 | // Tests the degree of the polynomial created is equal to the argument provided. 11 | func TestPolynomial(t *testing.T) { 12 | p := newPolynomial(polynomialTestDegree) 13 | if p.getDegree() != polynomialTestDegree { 14 | t.Errorf("degree of polynomial is not the provided") 15 | } 16 | } 17 | 18 | func TestCreateRandomPolynomial(t *testing.T) { 19 | p, err := createRandomPolynomial(polynomialTestDegree, big.NewInt(10), big.NewInt(1024)) 20 | if err != nil { 21 | t.Errorf("could not create a random polynomial") 22 | return 23 | } 24 | if p.getDegree() != polynomialTestDegree { 25 | t.Errorf("degree of polynomial is not the provided") 26 | } 27 | 28 | } 29 | 30 | func TestPolynomial_Eval(t *testing.T) { 31 | p := newPolynomial(polynomialTestDegree) 32 | p[3] = big.NewInt(7) 33 | p[2] = big.NewInt(5) 34 | p[1] = big.NewInt(9) 35 | p[0] = big.NewInt(1) 36 | 37 | expected := big.NewInt(7591) 38 | 39 | res := p.eval(big.NewInt(10)) 40 | 41 | if expected.Cmp(res) != 0 { 42 | t.Errorf("The evaluations is not providing a correct result") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /signature_share.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "crypto/sha256" 5 | "fmt" 6 | "math/big" 7 | ) 8 | 9 | // SigShare represents a signature share for a document. 10 | // It can be joined with other k signatures and generate a standard RSA signature. 11 | type SigShare struct { 12 | Xi []byte // Signature share. 13 | C []byte // Verification value. 14 | Z []byte // Verification value 15 | Id uint16 // ID of the node which generated the Signature Share. 16 | } 17 | 18 | // Signature is the completed signature of a document, created after 19 | // joining k signature shares. 20 | type Signature []byte 21 | 22 | // Verify verifies that a signature share was generated for the document provided and using a key 23 | // related to the key metadata provided. 24 | // It returns nil if the signature is valid, and an error if it is not. 25 | func (sigShare SigShare) Verify(doc []byte, info *KeyMeta) error { 26 | 27 | x := new(big.Int) 28 | xi := new(big.Int) 29 | z := new(big.Int) 30 | c := new(big.Int) 31 | c2 := new(big.Int) 32 | n := new(big.Int) 33 | e := new(big.Int) 34 | v := new(big.Int) 35 | u := new(big.Int) 36 | vki := new(big.Int) 37 | xTilde := new(big.Int) 38 | xi2 := new(big.Int) 39 | vPrime := new(big.Int) 40 | xPrime := new(big.Int) 41 | 42 | negC := new(big.Int) 43 | xiNeg2c := new(big.Int) 44 | aux := new(big.Int) 45 | 46 | x.SetBytes(doc) 47 | n.Set(info.PublicKey.N) 48 | e.SetUint64(uint64(info.PublicKey.E)) 49 | v.SetBytes(info.VerificationKey.V) 50 | u.SetBytes(info.VerificationKey.U) 51 | vki.SetBytes(info.VerificationKey.I[sigShare.Id-1]) 52 | 53 | xi.SetBytes(sigShare.Xi) 54 | z.SetBytes(sigShare.Z) 55 | c.SetBytes(sigShare.C) 56 | 57 | if big.Jacobi(x, n) == -1 { 58 | ue := new(big.Int).Exp(u, e, n) 59 | x.Mul(x, ue).Mod(x, n) 60 | } 61 | 62 | // x~ = x^4 % n 63 | xTilde.Exp(x, big.NewInt(4), n) 64 | 65 | // xi_2 = xi^2 % n 66 | xi2.Exp(xi, big.NewInt(2), n) 67 | 68 | // v' = v^z * v_i^(-c) 69 | negC.Neg(c) 70 | vPrime.Exp(vki, negC, n) 71 | aux.Exp(v, z, n) 72 | vPrime.Mul(vPrime, aux).Mod(vPrime, n) 73 | 74 | // x' = x~^z * x_i^(-2c) 75 | aux.Mul(negC, big.NewInt(2)) 76 | xiNeg2c.Exp(xi, aux, n) 77 | 78 | aux.Exp(xTilde, z, n) 79 | xPrime.Mul(aux, xiNeg2c) 80 | xPrime.Mod(xPrime, n) 81 | 82 | // Hashing all the values 83 | sha := sha256.New() 84 | sha.Write(v.Bytes()) 85 | sha.Write(u.Bytes()) 86 | sha.Write(xTilde.Bytes()) 87 | sha.Write(vki.Bytes()) 88 | sha.Write(xi2.Bytes()) 89 | sha.Write(vPrime.Bytes()) 90 | sha.Write(xPrime.Bytes()) 91 | 92 | hash := sha.Sum(nil) 93 | 94 | c2.SetBytes(hash) 95 | c2.Mod(c2, n) 96 | 97 | if c2.Cmp(c) == 0 { 98 | return nil 99 | } 100 | return fmt.Errorf("invalid signature share with id %d", sigShare.Id) 101 | } 102 | -------------------------------------------------------------------------------- /signature_share_list.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | ) 7 | 8 | // SigShareList is a list of sigShares ready to be joined. 9 | type SigShareList []*SigShare 10 | 11 | // Join generates a standard RSA signature using the signature shares of the document provided. 12 | // The number of signatures should be at least the number of threshold defined at key creation. 13 | // It returns the RSA signature generated, or an error if the process fails. 14 | func (sigShareList SigShareList) Join(document []byte, info *KeyMeta) (signature Signature, err error) { 15 | signature = make([]byte, info.PublicKey.Size()) 16 | if document == nil { 17 | err = fmt.Errorf("document is nil") 18 | return 19 | } 20 | if info == nil { 21 | err = fmt.Errorf("key metainfo is nil") 22 | return 23 | } 24 | 25 | if len(sigShareList) < int(info.K) { 26 | 27 | } 28 | 29 | for i := 0; i < len(sigShareList); i++ { 30 | if sigShareList[i] == nil { 31 | err = fmt.Errorf("signature share %d is nil", i) 32 | return 33 | } 34 | } 35 | 36 | k := info.K 37 | if len(sigShareList) < int(k) { 38 | err = fmt.Errorf("insufficient number of signature shares. provided: %d, needed: %d", len(sigShareList), k) 39 | return 40 | } 41 | 42 | x := new(big.Int) 43 | n := new(big.Int) 44 | e := new(big.Int) 45 | u := new(big.Int) 46 | delta := new(big.Int) 47 | ePrime := new(big.Int) 48 | w := new(big.Int) 49 | si := new(big.Int) 50 | aux := new(big.Int) 51 | a := new(big.Int) 52 | b := new(big.Int) 53 | wa := new(big.Int) 54 | xb := new(big.Int) 55 | y := new(big.Int) 56 | lambdaK2 := new(big.Int) 57 | 58 | x.SetBytes(document) 59 | n.Set(info.PublicKey.N) 60 | e.SetUint64(uint64(info.PublicKey.E)) 61 | u.SetBytes(info.VerificationKey.U) 62 | 63 | // x = doc if (doc | n) == 1 else doc * u^e 64 | 65 | jacobied := false 66 | 67 | if big.Jacobi(x, n) == -1 { 68 | ue := new(big.Int).Exp(u, e, n) 69 | x.Mul(x, ue).Mod(x, n) 70 | jacobied = true 71 | } else { 72 | } 73 | 74 | delta.MulRange(1, int64(info.L)) 75 | ePrime.SetInt64(4) 76 | 77 | // Calculate w 78 | w.SetInt64(1) 79 | 80 | var i uint16 81 | for i = 0; i < k; i++ { 82 | si.SetBytes(sigShareList[i].Xi) 83 | id := int64(sigShareList[i].Id) 84 | lambdaK2, err = sigShareList.lagrangeInterpolation(id, int64(k), delta) 85 | if err != nil { 86 | return 87 | } 88 | lambdaK2.Mul(lambdaK2, big.NewInt(2)) 89 | aux.Exp(si, lambdaK2, n) 90 | w.Mul(w, aux) 91 | } 92 | 93 | w.Mod(w, n) 94 | 95 | aux.GCD(a, b, ePrime, e) 96 | wa.Exp(w, a, n) 97 | xb.Exp(x, b, n) 98 | y.Mul(wa, xb) 99 | 100 | if jacobied { 101 | invU := new(big.Int).ModInverse(u, n) 102 | y.Mul(y, invU) 103 | } 104 | 105 | y.Mod(y, n) 106 | sig := y.Bytes() 107 | // Pads sig with zeros until pk size 108 | copy(signature[len(signature)-len(sig):], sig) 109 | return 110 | } 111 | 112 | // This function generates the lagrange interpolation for a set of signature shares. 113 | func (sigShareList SigShareList) lagrangeInterpolation(j, k int64, delta *big.Int) (*big.Int, error) { 114 | 115 | if int64(len(sigShareList)) < k { 116 | return new(big.Int), fmt.Errorf("signature shares are not enough. provided=%d, needed=%d", len(sigShareList), k) 117 | } 118 | out := new(big.Int) 119 | 120 | out.Set(delta) 121 | num := big.NewInt(1) 122 | den := big.NewInt(1) 123 | 124 | var i int64 125 | for i = 0; i < k; i++ { 126 | id := int64(sigShareList[i].Id) 127 | if id != j { 128 | num.Mul(num, big.NewInt(id)) // num <-- num*j_ 129 | den.Mul(den, big.NewInt(id-j)) // den <-- den*(j_-j) 130 | } 131 | } 132 | out.Mul(out, num) 133 | out.Div(out, den) 134 | 135 | return out, nil 136 | } 137 | -------------------------------------------------------------------------------- /signature_share_list_test.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "math/big" 5 | "testing" 6 | ) 7 | 8 | func TestSignatureShareList_LagrangeInterpolation(t *testing.T) { 9 | const signatureShareTestLength = 5 10 | const signatureShareTestK = 5 11 | const signatureShareTestM = 1024 12 | 13 | delta := new(big.Int) 14 | 15 | shares := make(SigShareList, signatureShareTestLength) 16 | 17 | var i uint16 18 | for i = 0; i < signatureShareTestLength; i++ { 19 | shares[i] = &SigShare{ 20 | Id: i + 1, 21 | } 22 | } 23 | delta.MulRange(1, signatureShareTestK) 24 | 25 | results := []*big.Int{ 26 | big.NewInt(600), 27 | big.NewInt(-1200), 28 | big.NewInt(1200), 29 | big.NewInt(-600), 30 | big.NewInt(120), 31 | } 32 | 33 | for i := 0; i < signatureShareTestK; i++ { 34 | res, err := shares.lagrangeInterpolation(int64(i+1), signatureShareTestK, delta) 35 | if err != nil { 36 | t.Errorf("couldn't compute lagrange interpolation") 37 | return 38 | } 39 | if res.Cmp(results[i]) != 0 { 40 | t.Errorf("lagrange interpolation for i=%d failed. It is %s but it should be %s", i+1, res, results[i]) 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /tcrsa_test.go: -------------------------------------------------------------------------------- 1 | package tcrsa_test 2 | 3 | import ( 4 | "crypto" 5 | "crypto/rsa" 6 | "crypto/sha256" 7 | "fmt" 8 | "github.com/niclabs/tcrsa" 9 | ) 10 | 11 | const exampleK = 3 12 | const exampleL = 5 13 | 14 | const exampleHashType = crypto.SHA256 15 | const exampleSize = 2048 16 | const exampleMessage = "Hello world" 17 | 18 | func Example() { 19 | // First we need to get the values of K and L from somewhere. 20 | k := uint16(exampleK) 21 | l := uint16(exampleL) 22 | 23 | // Generate keys provides to us with a list of keyShares and the key metainformation. 24 | keyShares, keyMeta, err := tcrsa.NewKey(exampleSize, uint16(k), uint16(l), nil) 25 | if err != nil { 26 | panic(fmt.Sprintf("%v", err)) 27 | } 28 | 29 | // Then we need to prepare the document we want to sign, so we hash it and pad it using PKCS v1.15. 30 | docHash := sha256.Sum256([]byte(exampleMessage)) 31 | docPKCS1, err := tcrsa.PrepareDocumentHash(keyMeta.PublicKey.Size(), crypto.SHA256, docHash[:]) 32 | if err != nil { 33 | panic(fmt.Sprintf("%v", err)) 34 | } 35 | 36 | sigShares := make(tcrsa.SigShareList, l) 37 | var i uint16 38 | 39 | // Now we sign with at least k nodes and check immediately the signature share for consistency. 40 | for i = 0; i < l; i++ { 41 | sigShares[i], err = keyShares[i].Sign(docPKCS1, exampleHashType, keyMeta) 42 | if err != nil { 43 | panic(fmt.Sprintf("%v", err)) 44 | } 45 | if err := sigShares[i].Verify(docPKCS1, keyMeta); err != nil { 46 | panic(fmt.Sprintf("%v", err)) 47 | } 48 | } 49 | 50 | // Having all the signature shares we needed, we join them to create a real signature. 51 | signature, err := sigShares.Join(docPKCS1, keyMeta) 52 | if err != nil { 53 | panic(fmt.Sprintf("%v", err)) 54 | } 55 | 56 | // Finally we check the signature with Golang's crypto/rsa PKCSv1.15 verification routine. 57 | if err := rsa.VerifyPKCS1v15(keyMeta.PublicKey, crypto.SHA256, docHash[:], signature); err != nil { 58 | panic(fmt.Sprintf("%v", err)) 59 | } 60 | fmt.Println("ok") 61 | // Output: ok 62 | } 63 | -------------------------------------------------------------------------------- /utils.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "io" 7 | "math/big" 8 | ) 9 | 10 | // Number of Miller-Rabin tests 11 | const c = 20 12 | 13 | // randInt is a function which generates a random big number, using crypto/rand 14 | // crypto-secure Golang library. 15 | func randInt(bitLen int) (randNum *big.Int, err error) { 16 | randNum = big.NewInt(0) 17 | if bitLen <= 0 { 18 | err = fmt.Errorf("bitlen should be greater than 0, but it is %d", bitLen) 19 | return 20 | } 21 | byteLen := bitLen / 8 22 | if bitLen % 8 != 0 { 23 | byteLen++ 24 | } 25 | rawRand := make([]byte, byteLen) 26 | 27 | for randNum.BitLen() == 0 || randNum.BitLen() > bitLen { 28 | _, err = rand.Read(rawRand) 29 | if err != nil { 30 | return 31 | } 32 | randNum.SetBytes(rawRand) 33 | // set MSBs to 0 to get a bitLen equal to bitLen param. 34 | for bit := bitLen; bit < randNum.BitLen(); bit++ { 35 | randNum.SetBit(randNum, bit, 0) 36 | } 37 | } 38 | 39 | if randNum.BitLen() == 0 || randNum.BitLen() > bitLen { 40 | err = fmt.Errorf("random number returned should have length at most %d, but its length is %d", bitLen, randNum.BitLen()) 41 | return 42 | } 43 | return 44 | } 45 | 46 | // generateSafePrimes generates two primes p and q, in a way that q 47 | // is equal to (p-1)/2. The greatest prime bit length is at least bitLen bits. 48 | func generateSafePrimes(bitLen int, randSource io.Reader) (*big.Int, *big.Int, error) { 49 | if randSource == nil { 50 | return big.NewInt(0), big.NewInt(0), fmt.Errorf("random source cannot be nil") 51 | } 52 | p := new(big.Int) 53 | 54 | for { 55 | q, err := rand.Prime(randSource, bitLen-1) 56 | if err != nil { 57 | return big.NewInt(0), big.NewInt(0), err 58 | } 59 | // p = 2q + 1 60 | p.Lsh(q, 1) 61 | p.SetBit(p,0,1) 62 | if p.ProbablyPrime(c) { 63 | return p, q, nil 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /utils_test.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | import ( 4 | "crypto/rand" 5 | "fmt" 6 | "math/big" 7 | "testing" 8 | "time" 9 | ) 10 | 11 | const utilsTestBitlen = 256 12 | 13 | // Miller-Rabin primality test rounds 14 | const utilsTestC = 25 15 | 16 | // Tests that two consecutive outputs from random dev are different. 17 | // TODO: Test how much different are the numbers generated 18 | func TestRandomDev_different(t *testing.T) { 19 | rand1, err := randInt(utilsTestBitlen) 20 | if err != nil { 21 | t.Errorf("first random number generation failed: %v", err) 22 | } 23 | rand2, err := randInt(utilsTestBitlen) 24 | if err != nil { 25 | t.Errorf("second random number generation failed: %v", err) 26 | } 27 | if rand1.Cmp(rand2) == 0 { 28 | t.Errorf("both random numbers are equal!") 29 | } 30 | } 31 | 32 | // Tests that the bit size of the output of a random dev function is the desired. 33 | func TestRandomDev_bitSize(t *testing.T) { 34 | rand1, err := randInt(utilsTestBitlen) 35 | if err != nil { 36 | t.Errorf("first random number generation failed: %v", err) 37 | } 38 | if rand1.BitLen() > utilsTestBitlen { 39 | t.Errorf("random number bit length should have been at most %d, but it was %d", rand1.BitLen(), utilsTestBitlen) 40 | } 41 | } 42 | 43 | func TestGenerateSafePrimes(t *testing.T) { 44 | 45 | pExpected := new(big.Int) 46 | 47 | p, pr, err := generateSafePrimes(utilsTestBitlen, rand.Reader) 48 | if err != nil { 49 | t.Errorf("safe prime generation failed: %v", err) 50 | } 51 | if !p.ProbablyPrime(utilsTestC) { 52 | t.Errorf("p is not prime") 53 | } 54 | if !pr.ProbablyPrime(utilsTestC) { 55 | t.Errorf("pr is not prime") 56 | } 57 | pExpected.Mul(pr, big.NewInt(2)).Add(pExpected, big.NewInt(1)) 58 | if p.Cmp(pExpected) != 0 { 59 | t.Errorf("p is not 2*pr + 1") 60 | } 61 | } 62 | 63 | func TestGenerateSafePrimes_keyGeneration(t *testing.T) { 64 | 65 | m := new(big.Int) 66 | d := new(big.Int) 67 | r := new(big.Int) 68 | 69 | _, pr, err := generateSafePrimes(utilsTestBitlen, rand.Reader) 70 | if err != nil { 71 | t.Errorf("safe prime generation failed: %v", err) 72 | } 73 | 74 | _, qr, err := generateSafePrimes(utilsTestBitlen, rand.Reader) 75 | if err != nil { 76 | t.Errorf("safe prime generation failed: %v", err) 77 | } 78 | 79 | m.Mul(pr, qr) 80 | e := big.NewInt(65537) 81 | 82 | d.ModInverse(e, m) 83 | r.Mul(d, e).Mod(r, m) 84 | 85 | if r.Cmp(big.NewInt(1)) != 0 { 86 | t.Errorf("safe prime generation failed") 87 | } 88 | 89 | } 90 | 91 | func TestGenerateSafePrimes_Time(t *testing.T) { 92 | for i := 4; i <11; i++ { 93 | keyLength := 1 << uint(i) 94 | start := time.Now() 95 | _, _, err := generateSafePrimes(keyLength, rand.Reader) 96 | if err != nil { 97 | t.Errorf("error generating safe primes: %d", err) 98 | } 99 | fmt.Printf("- %d byte safe prime pair obtained in %f seconds\n", keyLength, time.Since(start).Seconds()) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /verification_key.go: -------------------------------------------------------------------------------- 1 | package tcrsa 2 | 3 | // VerificationKey represents the data that is needed to verify a Key Share. 4 | // It groups all the verification values for all the nodes in I property. 5 | type VerificationKey struct { 6 | V []byte // Verification value. 7 | U []byte // Verification value. 8 | I [][]byte // An array of the verification values for the shares the nodes create when sign a document. 9 | } 10 | 11 | // NewVerificationKey generates an empty Verification Key structure, allocating 12 | // space for l verification values in I. 13 | func NewVerificationKey(l uint16) *VerificationKey { 14 | vk := &VerificationKey{ 15 | I: make([][]byte, l), 16 | } 17 | return vk 18 | } 19 | --------------------------------------------------------------------------------