-
Notifications
You must be signed in to change notification settings - Fork 141
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Adds RSA Threshold signatures (#364)
Adds RSA Threshold signatures: This is an implementation of "Practical Threshold Signatures" by Victor Shoup. * Minor changes related to PR comments * Formatting, remove md5 test * adds players and threshold to SignShare * Marshal/Unmarshal bug fixes, adds tests * minor fixes to keyshare * signShare fixes and more tests * minor fix Authored-by: Josh Brown <jbrown@cloudflare.com>
- Loading branch information
Showing
13 changed files
with
1,729 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# RSA Threshold Signatures | ||
|
||
This is an implementation of ["Practical Threshold Signatures" by Victor Shoup](https://www.iacr.org/archive/eurocrypt2000/1807/18070209-new.pdf). | ||
Protocol 1 is implemented. | ||
|
||
## Threshold Primer | ||
|
||
Let *l* be the total number of players, *t* be the number of corrupted players, and *k* be the threshold. | ||
The idea of threshold signatures is that at least *k* players need to participate to form a valid signature. | ||
|
||
Setup consists of a dealer generating *l* key shares from a key pair and "dealing" them to the players. In this implementation the dealer is trusted. | ||
|
||
During the signing phase, at least *k* players use their key share and the message to generate a signature share. | ||
Finally, the *k* signature shares are combined to form a valid signature for the message. | ||
|
||
## Modifications | ||
|
||
1. Our implementation is not robust. That is, the corrupted players can prevent a valid signature from being formed by the non-corrupted players. As such, we remove all verification. | ||
2. The paper requires p and q to be safe primes. We do not. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/crypto/rsa/pkcs1v15.go | ||
|
||
// Copyright (c) 2009 The Go Authors. All rights reserved. | ||
// | ||
// Redistribution and use in source and binary forms, with or without | ||
// modification, are permitted provided that the following conditions are | ||
// met: | ||
// | ||
// * Redistributions of source code must retain the above copyright | ||
// notice, this list of conditions and the following disclaimer. | ||
// * Redistributions in binary form must reproduce the above | ||
// copyright notice, this list of conditions and the following disclaimer | ||
// in the documentation and/or other materials provided with the | ||
// distribution. | ||
// * Neither the name of Google Inc. nor the names of its | ||
// contributors may be used to endorse or promote products derived from | ||
// this software without specific prior written permission. | ||
// | ||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
// Copyright 2009 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package internal | ||
|
||
import ( | ||
"crypto" | ||
"crypto/rsa" | ||
"errors" | ||
"fmt" | ||
) | ||
|
||
var hashPrefixes = map[crypto.Hash][]byte{ | ||
crypto.MD5: { | ||
0x30, 0x20, 0x30, 0x0c, 0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05, 0x05, | ||
0x00, 0x04, 0x10, | ||
}, | ||
crypto.SHA1: {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14}, | ||
crypto.SHA224: { | ||
0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04, | ||
0x05, 0x00, 0x04, 0x1c, | ||
}, | ||
crypto.SHA256: { | ||
0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, | ||
0x05, 0x00, 0x04, 0x20, | ||
}, | ||
crypto.SHA384: { | ||
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, | ||
0x05, 0x00, 0x04, 0x30, | ||
}, | ||
crypto.SHA512: { | ||
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, | ||
0x05, 0x00, 0x04, 0x40, | ||
}, | ||
crypto.MD5SHA1: {}, // A special TLS case which doesn't use an ASN1 prefix. | ||
crypto.RIPEMD160: {0x30, 0x20, 0x30, 0x08, 0x06, 0x06, 0x28, 0xcf, 0x06, 0x03, 0x00, 0x31, 0x04, 0x14}, | ||
} | ||
|
||
func PadPKCS1v15(pub *rsa.PublicKey, hash crypto.Hash, hashed []byte) ([]byte, error) { | ||
hashLen, prefix, err := pkcs1v15HashInfo(hash, len(hashed)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
tLen := len(prefix) + hashLen | ||
k := pub.Size() | ||
if k < tLen+11 { | ||
return nil, fmt.Errorf("message too long") | ||
} | ||
|
||
// EM = 0x00 || 0x01 || PS || 0x00 || T | ||
em := make([]byte, k) | ||
em[1] = 1 | ||
for i := 2; i < k-tLen-1; i++ { | ||
em[i] = 0xff | ||
} | ||
copy(em[k-tLen:k-hashLen], prefix) | ||
copy(em[k-hashLen:k], hashed) | ||
|
||
return em, nil | ||
} | ||
|
||
func pkcs1v15HashInfo(hash crypto.Hash, inLen int) (hashLen int, prefix []byte, err error) { | ||
// Special case: crypto.Hash(0) is used to indicate that the data is | ||
// signed directly. | ||
if hash == 0 { | ||
return inLen, nil, nil | ||
} | ||
|
||
hashLen = hash.Size() | ||
if inLen != hashLen { | ||
return 0, nil, errors.New("threshold_internal: crypto/rsa: input must be hashed message") | ||
} | ||
prefix, ok := hashPrefixes[hash] | ||
if !ok { | ||
return 0, nil, errors.New("threshold_internal: crypto/rsa: unsupported hash function") | ||
} | ||
return | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/crypto/rsa/pss.go | ||
|
||
// Copyright (c) 2009 The Go Authors. All rights reserved. | ||
// | ||
// Redistribution and use in source and binary forms, with or without | ||
// modification, are permitted provided that the following conditions are | ||
// met: | ||
// | ||
// * Redistributions of source code must retain the above copyright | ||
// notice, this list of conditions and the following disclaimer. | ||
// * Redistributions in binary form must reproduce the above | ||
// copyright notice, this list of conditions and the following disclaimer | ||
// in the documentation and/or other materials provided with the | ||
// distribution. | ||
// * Neither the name of Google Inc. nor the names of its | ||
// contributors may be used to endorse or promote products derived from | ||
// this software without specific prior written permission. | ||
// | ||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
// Copyright 2013 The Go Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style | ||
// license that can be found in the LICENSE file. | ||
|
||
package pss | ||
|
||
// This file implements the RSASSA-PSS signature scheme according to RFC 8017. | ||
|
||
import ( | ||
"crypto" | ||
"crypto/rsa" | ||
"errors" | ||
"hash" | ||
"io" | ||
) | ||
|
||
// Per RFC 8017, Section 9.1 | ||
// | ||
// EM = MGF1 xor DB || H( 8*0x00 || mHash || salt ) || 0xbc | ||
// | ||
// where | ||
// | ||
// DB = PS || 0x01 || salt | ||
// | ||
// and PS can be empty so | ||
// | ||
// emLen = dbLen + hLen + 1 = psLen + sLen + hLen + 2 | ||
// | ||
|
||
func emsaPSSEncode(mHash []byte, emBits int, salt []byte, hash hash.Hash) ([]byte, error) { | ||
// See RFC 8017, Section 9.1.1. | ||
|
||
hLen := hash.Size() | ||
sLen := len(salt) | ||
emLen := (emBits + 7) / 8 | ||
|
||
// 1. If the length of M is greater than the input limitation for the | ||
// hash function (2^61 - 1 octets for SHA-1), output "message too | ||
// long" and stop. | ||
// | ||
// 2. Let mHash = Hash(M), an octet string of length hLen. | ||
|
||
if len(mHash) != hLen { | ||
return nil, errors.New("crypto/rsa: input must be hashed with given hash") | ||
} | ||
|
||
// 3. If emLen < hLen + sLen + 2, output "encoding error" and stop. | ||
|
||
if emLen < hLen+sLen+2 { | ||
return nil, errors.New("crypto/rsa: key size too small for PSS signature") | ||
} | ||
|
||
em := make([]byte, emLen) | ||
psLen := emLen - sLen - hLen - 2 | ||
db := em[:psLen+1+sLen] | ||
h := em[psLen+1+sLen : emLen-1] | ||
|
||
// 4. Generate a random octet string salt of length sLen; if sLen = 0, | ||
// then salt is the empty string. | ||
// | ||
// 5. Let | ||
// M' = (0x)00 00 00 00 00 00 00 00 || mHash || salt; | ||
// | ||
// M' is an octet string of length 8 + hLen + sLen with eight | ||
// initial zero octets. | ||
// | ||
// 6. Let H = Hash(M'), an octet string of length hLen. | ||
|
||
var prefix [8]byte | ||
|
||
hash.Write(prefix[:]) | ||
hash.Write(mHash) | ||
hash.Write(salt) | ||
|
||
h = hash.Sum(h[:0]) | ||
hash.Reset() | ||
|
||
// 7. Generate an octet string PS consisting of emLen - sLen - hLen - 2 | ||
// zero octets. The length of PS may be 0. | ||
// | ||
// 8. Let DB = PS || 0x01 || salt; DB is an octet string of length | ||
// emLen - hLen - 1. | ||
|
||
db[psLen] = 0x01 | ||
copy(db[psLen+1:], salt) | ||
|
||
// 9. Let dbMask = MGF(H, emLen - hLen - 1). | ||
// | ||
// 10. Let maskedDB = DB \xor dbMask. | ||
|
||
mgf1XOR(db, hash, h) | ||
|
||
// 11. Set the leftmost 8 * emLen - emBits bits of the leftmost octet in | ||
// maskedDB to zero. | ||
|
||
db[0] &= 0xff >> (8*emLen - emBits) | ||
|
||
// 12. Let EM = maskedDB || H || 0xbc. | ||
em[emLen-1] = 0xbc | ||
|
||
// 13. Output EM. | ||
return em, nil | ||
} | ||
|
||
func padPSSWithSalt(pub *rsa.PublicKey, hash crypto.Hash, hashed, salt []byte) ([]byte, error) { | ||
emBits := pub.N.BitLen() - 1 | ||
em, err := emsaPSSEncode(hashed, emBits, salt, hash.New()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return em, nil | ||
} | ||
|
||
func saltLength(opts *rsa.PSSOptions) int { | ||
if opts == nil { | ||
return rsa.PSSSaltLengthAuto | ||
} | ||
return opts.SaltLength | ||
} | ||
|
||
func PadPSS(rand io.Reader, pub *rsa.PublicKey, hash crypto.Hash, digest []byte, opts *rsa.PSSOptions) ([]byte, error) { | ||
if opts != nil && opts.Hash != 0 { | ||
hash = opts.Hash | ||
} | ||
|
||
saltLength := saltLength(opts) | ||
switch saltLength { | ||
case rsa.PSSSaltLengthAuto: | ||
saltLength = (pub.N.BitLen()-1+7)/8 - 2 - hash.Size() | ||
case rsa.PSSSaltLengthEqualsHash: | ||
saltLength = hash.Size() | ||
} | ||
|
||
salt := make([]byte, saltLength) | ||
if _, err := io.ReadFull(rand, salt); err != nil { | ||
return nil, err | ||
} | ||
return padPSSWithSalt(pub, hash, digest, salt) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
// https://cs.opensource.google/go/go/+/refs/tags/go1.18.3:src/crypto/rsa/rsa.go | ||
|
||
// Copyright (c) 2009 The Go Authors. All rights reserved. | ||
// | ||
// Redistribution and use in source and binary forms, with or without | ||
// modification, are permitted provided that the following conditions are | ||
// met: | ||
// | ||
// * Redistributions of source code must retain the above copyright | ||
// notice, this list of conditions and the following disclaimer. | ||
// * Redistributions in binary form must reproduce the above | ||
// copyright notice, this list of conditions and the following disclaimer | ||
// in the documentation and/or other materials provided with the | ||
// distribution. | ||
// * Neither the name of Google Inc. nor the names of its | ||
// contributors may be used to endorse or promote products derived from | ||
// this software without specific prior written permission. | ||
// | ||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
|
||
package pss | ||
|
||
import ( | ||
"hash" | ||
) | ||
|
||
// incCounter increments a four byte, big-endian counter. | ||
func incCounter(c *[4]byte) { | ||
if c[3]++; c[3] != 0 { | ||
return | ||
} | ||
if c[2]++; c[2] != 0 { | ||
return | ||
} | ||
if c[1]++; c[1] != 0 { | ||
return | ||
} | ||
c[0]++ | ||
} | ||
|
||
// mgf1XOR XORs the bytes in out with a mask generated using the MGF1 function | ||
// specified in PKCS #1 v2.1. | ||
func mgf1XOR(out []byte, hash hash.Hash, seed []byte) { | ||
var counter [4]byte | ||
var digest []byte | ||
|
||
done := 0 | ||
for done < len(out) { | ||
hash.Write(seed) | ||
hash.Write(counter[0:4]) | ||
digest = hash.Sum(digest[:0]) | ||
hash.Reset() | ||
|
||
for i := 0; i < len(digest) && done < len(out); i++ { | ||
out[done] ^= digest[i] | ||
done++ | ||
} | ||
incCounter(&counter) | ||
} | ||
} |
Oops, something went wrong.