2017-04-25 00:19:04 +00:00
|
|
|
package moneroutil
|
|
|
|
|
2017-05-05 03:11:50 +00:00
|
|
|
import (
|
|
|
|
"bytes"
|
2017-05-11 02:56:17 +00:00
|
|
|
"crypto/rand"
|
2017-05-05 03:11:50 +00:00
|
|
|
"fmt"
|
2017-05-11 15:22:38 +00:00
|
|
|
mathrand "math/rand"
|
2017-05-05 03:11:50 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
2017-05-11 02:56:17 +00:00
|
|
|
PointLength = 32
|
|
|
|
ScalarLength = 32
|
2017-05-05 03:11:50 +00:00
|
|
|
)
|
|
|
|
|
2017-05-11 02:56:17 +00:00
|
|
|
type PubKey [PointLength]byte
|
|
|
|
type PrivKey [ScalarLength]byte
|
|
|
|
|
2017-05-05 03:11:50 +00:00
|
|
|
type RingSignatureElement struct {
|
2017-05-11 02:56:17 +00:00
|
|
|
c [ScalarLength]byte
|
|
|
|
r [ScalarLength]byte
|
2017-05-05 03:11:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type RingSignature []*RingSignatureElement
|
|
|
|
|
2017-05-11 02:56:17 +00:00
|
|
|
func RandomScalar() (result [ScalarLength]byte) {
|
|
|
|
var reduceFrom [ScalarLength * 2]byte
|
|
|
|
tmp := make([]byte, ScalarLength*2)
|
|
|
|
rand.Read(tmp)
|
|
|
|
copy(reduceFrom[:], tmp)
|
2017-05-11 15:28:30 +00:00
|
|
|
ScReduce(&result, &reduceFrom)
|
2017-05-05 03:11:50 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-05-11 15:22:38 +00:00
|
|
|
func (p *PrivKey) FromBytes(b [ScalarLength]byte) {
|
|
|
|
*p = b
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PrivKey) ToBytes() (result [ScalarLength]byte) {
|
|
|
|
result = [32]byte(*p)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PrivKey) PubKey() (pubKey *PubKey) {
|
|
|
|
secret := p.ToBytes()
|
2017-05-11 15:28:30 +00:00
|
|
|
point := new(ExtendedGroupElement)
|
|
|
|
GeScalarMultBase(point, &secret)
|
2017-05-11 15:22:38 +00:00
|
|
|
pubKeyBytes := new([PointLength]byte)
|
|
|
|
point.ToBytes(pubKeyBytes)
|
|
|
|
pubKey = (*PubKey)(pubKeyBytes)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *PubKey) ToBytes() (result [PointLength]byte) {
|
|
|
|
result = [PointLength]byte(*p)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewKeyPair() (privKey *PrivKey, pubKey *PubKey) {
|
|
|
|
privKey = new(PrivKey)
|
|
|
|
pubKey = new(PubKey)
|
|
|
|
privKey.FromBytes(RandomScalar())
|
|
|
|
pubKey = privKey.PubKey()
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-05-05 03:11:50 +00:00
|
|
|
func (s *RingSignatureElement) Serialize() (result []byte) {
|
2017-05-11 02:56:17 +00:00
|
|
|
result = make([]byte, 2*ScalarLength)
|
2017-05-05 03:11:50 +00:00
|
|
|
copy(result, s.c[:])
|
2017-05-11 02:56:17 +00:00
|
|
|
copy(result[ScalarLength:2*ScalarLength], s.r[:])
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func (r *RingSignature) Serialize() (result []byte) {
|
|
|
|
for i := 0; i < len(*r); i++ {
|
|
|
|
result = append(result, (*r)[i].Serialize()...)
|
|
|
|
}
|
2017-05-05 03:11:50 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseSignature(buf *bytes.Buffer) (result *RingSignatureElement, err error) {
|
|
|
|
s := new(RingSignatureElement)
|
2017-05-11 02:56:17 +00:00
|
|
|
c := buf.Next(ScalarLength)
|
|
|
|
if len(c) != ScalarLength {
|
2017-05-10 04:18:37 +00:00
|
|
|
err = fmt.Errorf("Not enough bytes for signature c")
|
2017-05-05 03:11:50 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
copy(s.c[:], c)
|
2017-05-11 02:56:17 +00:00
|
|
|
r := buf.Next(ScalarLength)
|
|
|
|
if len(r) != ScalarLength {
|
2017-05-10 04:18:37 +00:00
|
|
|
err = fmt.Errorf("Not enough bytes for signature r")
|
2017-05-05 03:11:50 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
copy(s.r[:], r)
|
|
|
|
result = s
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func ParseSignatures(mixinLengths []int, buf *bytes.Buffer) (signatures []RingSignature, err error) {
|
|
|
|
// mixinLengths is the number of mixins at each input position
|
|
|
|
sigs := make([]RingSignature, len(mixinLengths), len(mixinLengths))
|
|
|
|
for i, nMixin := range mixinLengths {
|
|
|
|
sigs[i] = make([]*RingSignatureElement, nMixin, nMixin)
|
|
|
|
for j := 0; j < nMixin; j++ {
|
|
|
|
sigs[i][j], err = ParseSignature(buf)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
signatures = sigs
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// hashes a pubkey into an Edwards Curve element
|
2017-05-11 15:28:30 +00:00
|
|
|
func HashToEC(pk *PubKey, r *ExtendedGroupElement) {
|
|
|
|
var p1 ProjectiveGroupElement
|
|
|
|
var p2 CompletedGroupElement
|
2017-05-11 02:56:17 +00:00
|
|
|
h := [PointLength]byte(Keccak256(pk[:]))
|
2017-05-10 14:17:19 +00:00
|
|
|
p1.FromBytes(&h)
|
2017-05-11 15:28:30 +00:00
|
|
|
GeMul8(&p2, &p1)
|
2017-05-05 03:11:50 +00:00
|
|
|
p2.ToExtended(r)
|
|
|
|
}
|
|
|
|
|
2017-05-11 02:56:17 +00:00
|
|
|
func HashToScalar(data ...[]byte) (result [ScalarLength]byte) {
|
2017-05-10 04:18:37 +00:00
|
|
|
result = Keccak256(data...)
|
2017-05-11 15:28:30 +00:00
|
|
|
ScReduce32(&result)
|
2017-05-10 04:18:37 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2017-05-11 15:22:38 +00:00
|
|
|
func CreateSignature(prefixHash *Hash, mixins []PubKey, privKey *PrivKey) (keyImage PubKey, pubKeys []PubKey, sig RingSignature) {
|
2017-05-11 15:28:30 +00:00
|
|
|
point := new(ExtendedGroupElement)
|
2017-05-11 15:22:38 +00:00
|
|
|
HashToEC(privKey.PubKey(), point)
|
|
|
|
privKeyBytes := privKey.ToBytes()
|
2017-05-11 15:28:30 +00:00
|
|
|
keyImagePoint := new(ProjectiveGroupElement)
|
|
|
|
GeScalarMult(keyImagePoint, &privKeyBytes, point)
|
2017-05-11 15:22:38 +00:00
|
|
|
var keyImageBytes [PointLength]byte
|
|
|
|
// convert key Image point from Projective to Extended
|
|
|
|
// in order to precompute
|
|
|
|
keyImagePoint.ToBytes(&keyImageBytes)
|
2017-05-11 15:28:30 +00:00
|
|
|
keyImageGe := new(ExtendedGroupElement)
|
2017-05-11 02:56:17 +00:00
|
|
|
keyImageGe.FromBytes(&keyImageBytes)
|
2017-05-11 15:22:38 +00:00
|
|
|
keyImage = PubKey(keyImageBytes)
|
2017-05-11 15:28:30 +00:00
|
|
|
var keyImagePre [8]CachedGroupElement
|
|
|
|
GePrecompute(&keyImagePre, keyImageGe)
|
2017-05-11 02:56:17 +00:00
|
|
|
k := RandomScalar()
|
2017-05-11 15:22:38 +00:00
|
|
|
pubKeys = make([]PubKey, len(mixins)+1)
|
|
|
|
privIndex := mathrand.Intn(len(pubKeys))
|
|
|
|
pubKeys[privIndex] = *privKey.PubKey()
|
2017-05-11 02:56:17 +00:00
|
|
|
r := make([]*RingSignatureElement, len(pubKeys))
|
2017-05-11 15:22:38 +00:00
|
|
|
var sum [ScalarLength]byte
|
|
|
|
toHash := prefixHash[:]
|
|
|
|
for i := 0; i < len(pubKeys); i++ {
|
2017-05-11 15:28:30 +00:00
|
|
|
tmpE := new(ExtendedGroupElement)
|
|
|
|
tmpP := new(ProjectiveGroupElement)
|
2017-05-11 02:56:17 +00:00
|
|
|
var tmpEBytes, tmpPBytes [PointLength]byte
|
|
|
|
if i == privIndex {
|
2017-05-11 15:28:30 +00:00
|
|
|
GeScalarMultBase(tmpE, &k)
|
2017-05-11 02:56:17 +00:00
|
|
|
tmpE.ToBytes(&tmpEBytes)
|
|
|
|
toHash = append(toHash, tmpEBytes[:]...)
|
2017-05-11 15:22:38 +00:00
|
|
|
HashToEC(privKey.PubKey(), tmpE)
|
2017-05-11 15:28:30 +00:00
|
|
|
GeScalarMult(tmpP, &k, tmpE)
|
2017-05-11 02:56:17 +00:00
|
|
|
tmpP.ToBytes(&tmpPBytes)
|
|
|
|
toHash = append(toHash, tmpPBytes[:]...)
|
|
|
|
} else {
|
2017-05-11 15:22:38 +00:00
|
|
|
if i > privIndex {
|
|
|
|
pubKeys[i] = mixins[i-1]
|
|
|
|
} else {
|
|
|
|
pubKeys[i] = mixins[i]
|
|
|
|
}
|
2017-05-11 02:56:17 +00:00
|
|
|
r[i] = &RingSignatureElement{
|
|
|
|
c: RandomScalar(),
|
|
|
|
r: RandomScalar(),
|
|
|
|
}
|
2017-05-11 15:22:38 +00:00
|
|
|
pubKeyBytes := pubKeys[i].ToBytes()
|
2017-05-11 02:56:17 +00:00
|
|
|
tmpE.FromBytes(&pubKeyBytes)
|
2017-05-11 15:28:30 +00:00
|
|
|
GeDoubleScalarMultVartime(tmpP, &r[i].c, tmpE, &r[i].r)
|
2017-05-11 02:56:17 +00:00
|
|
|
tmpP.ToBytes(&tmpPBytes)
|
|
|
|
toHash = append(toHash, tmpPBytes[:]...)
|
2017-05-11 15:22:38 +00:00
|
|
|
HashToEC(&pubKeys[i], tmpE)
|
2017-05-11 15:28:30 +00:00
|
|
|
GeDoubleScalarMultPrecompVartime(tmpP, &r[i].r, tmpE, &r[i].c, &keyImagePre)
|
2017-05-11 02:56:17 +00:00
|
|
|
tmpP.ToBytes(&tmpPBytes)
|
|
|
|
toHash = append(toHash, tmpPBytes[:]...)
|
2017-05-11 15:28:30 +00:00
|
|
|
ScAdd(&sum, &sum, &r[i].c)
|
2017-05-11 02:56:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
h := HashToScalar(toHash)
|
|
|
|
r[privIndex] = new(RingSignatureElement)
|
2017-05-11 15:28:30 +00:00
|
|
|
ScSub(&r[privIndex].c, &h, &sum)
|
2017-05-11 15:22:38 +00:00
|
|
|
scalar := privKey.ToBytes()
|
2017-05-11 15:28:30 +00:00
|
|
|
ScMulSub(&r[privIndex].r, &r[privIndex].c, &scalar, &k)
|
2017-05-11 15:22:38 +00:00
|
|
|
sig = r
|
2017-05-11 02:56:17 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func VerifySignature(prefixHash *Hash, keyImage *PubKey, pubKeys []PubKey, ringSignature RingSignature) (result bool) {
|
2017-05-11 15:28:30 +00:00
|
|
|
keyImageGe := new(ExtendedGroupElement)
|
2017-05-11 02:56:17 +00:00
|
|
|
keyImageBytes := [PointLength]byte(*keyImage)
|
2017-05-10 15:14:12 +00:00
|
|
|
if !keyImageGe.FromBytes(&keyImageBytes) {
|
|
|
|
result = false
|
|
|
|
return
|
|
|
|
}
|
2017-05-11 15:28:30 +00:00
|
|
|
var keyImagePre [8]CachedGroupElement
|
|
|
|
GePrecompute(&keyImagePre, keyImageGe)
|
2017-05-05 03:11:50 +00:00
|
|
|
toHash := prefixHash[:]
|
2017-05-11 02:56:17 +00:00
|
|
|
var tmpS, sum [ScalarLength]byte
|
2017-05-05 03:11:50 +00:00
|
|
|
for i, pubKey := range pubKeys {
|
2017-05-11 02:56:17 +00:00
|
|
|
rse := ringSignature[i]
|
2017-05-11 15:28:30 +00:00
|
|
|
if !ScValid(&rse.c) || !ScValid(&rse.r) {
|
2017-05-10 15:14:12 +00:00
|
|
|
result = false
|
|
|
|
return
|
|
|
|
}
|
2017-05-11 15:28:30 +00:00
|
|
|
tmpE := new(ExtendedGroupElement)
|
|
|
|
tmpP := new(ProjectiveGroupElement)
|
2017-05-11 02:56:17 +00:00
|
|
|
pubKeyBytes := [PointLength]byte(pubKey)
|
2017-05-10 15:14:12 +00:00
|
|
|
if !tmpE.FromBytes(&pubKeyBytes) {
|
|
|
|
result = false
|
|
|
|
return
|
|
|
|
}
|
2017-05-11 02:56:17 +00:00
|
|
|
var tmpPBytes, tmpEBytes [PointLength]byte
|
2017-05-11 15:28:30 +00:00
|
|
|
GeDoubleScalarMultVartime(tmpP, &rse.c, tmpE, &rse.r)
|
2017-05-05 03:11:50 +00:00
|
|
|
tmpP.ToBytes(&tmpPBytes)
|
|
|
|
toHash = append(toHash, tmpPBytes[:]...)
|
2017-05-11 02:56:17 +00:00
|
|
|
HashToEC(&pubKey, tmpE)
|
2017-05-05 03:11:50 +00:00
|
|
|
tmpE.ToBytes(&tmpEBytes)
|
2017-05-11 15:28:30 +00:00
|
|
|
GeDoubleScalarMultPrecompVartime(tmpP, &rse.r, tmpE, &rse.c, &keyImagePre)
|
2017-05-05 03:11:50 +00:00
|
|
|
tmpP.ToBytes(&tmpPBytes)
|
|
|
|
toHash = append(toHash, tmpPBytes[:]...)
|
2017-05-11 15:28:30 +00:00
|
|
|
ScAdd(&sum, &sum, &rse.c)
|
2017-05-05 03:11:50 +00:00
|
|
|
}
|
2017-05-10 04:18:37 +00:00
|
|
|
tmpS = HashToScalar(toHash)
|
2017-05-11 15:28:30 +00:00
|
|
|
ScSub(&sum, &tmpS, &sum)
|
|
|
|
result = ScIsZero(&sum)
|
2017-05-05 03:11:50 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Below are structs for the CT version */
|
|
|
|
|
2017-04-25 00:19:04 +00:00
|
|
|
type Key [32]byte
|
|
|
|
|
|
|
|
type ctKey struct {
|
|
|
|
destination Key
|
|
|
|
mask Key
|
|
|
|
}
|
|
|
|
|
|
|
|
type ecdhTuple struct {
|
|
|
|
mask Key
|
|
|
|
amount Key
|
|
|
|
senderPk Key
|
|
|
|
}
|
|
|
|
|
|
|
|
type RingSignatureBase struct {
|
|
|
|
ringSigType uint8
|
|
|
|
message Key
|
|
|
|
mixRing [][]ctKey
|
|
|
|
pseudoOuts []Key
|
|
|
|
ecdhInfo []ecdhTuple
|
|
|
|
outPk []ctKey
|
|
|
|
fee uint64
|
|
|
|
}
|
|
|
|
|
|
|
|
type Key64 [64]Key
|
|
|
|
|
|
|
|
type boroSig struct {
|
|
|
|
s0 Key64
|
|
|
|
s1 Key64
|
|
|
|
ee Key
|
|
|
|
}
|
|
|
|
|
|
|
|
type mgSig struct {
|
|
|
|
ss [][]Key
|
|
|
|
cc Key
|
|
|
|
ii []Key
|
|
|
|
}
|
|
|
|
|
|
|
|
type rangeSig struct {
|
|
|
|
asig boroSig
|
|
|
|
ci Key64
|
|
|
|
}
|
|
|
|
|
|
|
|
type RctSigPrunable struct {
|
|
|
|
rangeSigs []rangeSig
|
|
|
|
MGs []mgSig
|
|
|
|
}
|
|
|
|
|
2017-05-05 03:11:50 +00:00
|
|
|
type RingSignatureCT struct {
|
2017-04-25 00:19:04 +00:00
|
|
|
RingSignatureBase
|
|
|
|
RctSigPrunable
|
|
|
|
}
|