WIP: getting ringsignatures to work

This commit is contained in:
Jimmy Song 2017-05-04 20:11:50 -07:00
parent f159179cc4
commit cbbfb176fe
8 changed files with 287 additions and 70 deletions

View file

@ -12,8 +12,8 @@ type Address struct {
func (a *Address) Base58() (result string) {
prefix := []byte{byte(a.network)}
checksum := Checksum(prefix, a.spendingKey, a.viewingKey)
result = EncodeMoneroBase58(prefix, a.spendingKey, a.viewingKey, checksum)
checksum := GetChecksum(prefix, a.spendingKey, a.viewingKey)
result = EncodeMoneroBase58(prefix, a.spendingKey, a.viewingKey, checksum[:])
return
}
@ -23,8 +23,8 @@ func NewAddress(address string) (result *Address, err string) {
err = "Address is the wrong length"
return
}
checksum := Checksum(raw[:65])
if bytes.Compare(checksum, raw[65:]) != 0 {
checksum := GetChecksum(raw[:65])
if bytes.Compare(checksum[:], raw[65:]) != 0 {
err = "Checksum does not validate"
return
}

11
internal_test.go Normal file
View file

@ -0,0 +1,11 @@
package moneroutil
import (
"encoding/hex"
)
func HexToBytes(h string) (result [32]byte) {
byteSlice, _ := hex.DecodeString(h)
copy(result[:], byteSlice)
return
}

View file

@ -4,17 +4,36 @@ import (
"github.com/ebfe/keccak"
)
func Keccak256(data ...[]byte) (result []byte) {
const (
ChecksumLength = 4
HashLength = 32
)
type Hash [HashLength]byte
type Checksum [ChecksumLength]byte
func Keccak256(data ...[]byte) (result Hash) {
h := keccak.New256()
for _, b := range data {
h.Write(b)
}
result = h.Sum(nil)
r := h.Sum(nil)
copy(result[:], r)
return
}
func Checksum(data ...[]byte) (result []byte) {
func GetChecksum(data ...[]byte) (result Checksum) {
keccak256 := Keccak256(data...)
result = keccak256[:4]
copy(result[:], keccak256[:4])
return
}
func Keccak512(data ...[]byte) (result Hash) {
h := keccak.New512()
for _, b := range data {
h.Write(b)
}
r := h.Sum(nil)
copy(result[:], r)
return
}

39
keccak_test.go Normal file
View file

@ -0,0 +1,39 @@
package moneroutil
import (
"bytes"
"encoding/hex"
"testing"
)
func TestKeccak256(t *testing.T) {
tests := []struct {
name string
messageHex string
wantHex string
}{
{
name: "from monero 1",
messageHex: "c8fedd380dbae40ffb52",
wantHex: "8e41962058b7422e7404253121489a3e63d186ed115086919a75105661483ba9",
},
{
name: "from monero 2",
messageHex: "5020c4d530b6ec6cb4d9",
wantHex: "8a597f11961935e32e0adeab2ce48b3df2d907c9b26619dad22f42ff65ab7593",
},
{
name: "from monero cryptotest.pl",
messageHex: "0f3fe9c20b24a11bf4d6d1acd335c6a80543f1f0380590d7323caf1390c78e88",
wantHex: "73b7a236f2a97c4e1805f7a319f1283e3276598567757186c526caf9a49e0a92",
},
}
for _, test := range tests {
message, _ := hex.DecodeString(test.messageHex)
got := Keccak256(message)
want := HexToBytes(test.wantHex)
if bytes.Compare(want[:], got[:]) != 0 {
t.Errorf("want %x, got %x", want, got)
}
}
}

View file

@ -1,5 +1,123 @@
package moneroutil
import (
"bytes"
"errors"
"fmt"
"github.com/paxos-bankchain/ed25519/edwards25519"
)
const (
PubKeyLength = 32
)
type PubKey [PubKeyLength]byte
type RingSignatureElement struct {
c [PubKeyLength]byte
r [PubKeyLength]byte
}
type RingSignature []*RingSignatureElement
func Reverse(orig [32]byte) (result [32]byte) {
for i, j := 0, 31; i < j; i, j = i+1, j-1 {
result[i], result[j] = orig[j], orig[i]
}
return
}
func (s *RingSignatureElement) Serialize() (result []byte) {
result = make([]byte, 64)
copy(result, s.c[:])
copy(result[32:64], s.r[:])
return
}
func ParseSignature(buf *bytes.Buffer) (result *RingSignatureElement, err error) {
s := new(RingSignatureElement)
c := buf.Next(PubKeyLength)
if len(c) != PubKeyLength {
err = errors.New("Not enough bytes for signature c")
return
}
copy(s.c[:], c)
r := buf.Next(PubKeyLength)
if len(r) != PubKeyLength {
err = errors.New("Not enough bytes for signature r")
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
func HashToEC(pk PubKey, r *edwards25519.ExtendedGroupElement) {
var p1 edwards25519.ExtendedGroupElement
var p2 edwards25519.CompletedGroupElement
h := [32]byte(Keccak256(pk[:]))
for !p1.FromBytes(&h) {
h[0]++
}
edwards25519.GeMul8(&p2, &p1)
p2.ToExtended(r)
var tmp [32]byte
r.ToBytes(&tmp)
}
func VerifySignature(prefixHash Hash, keyImage PubKey, pubKeys []PubKey, ringSignature RingSignature) (result bool) {
keyImageGe := new(edwards25519.ExtendedGroupElement)
keyImageBytes := [32]byte(keyImage)
keyImageGe.FromBytes(&keyImageBytes)
var keyImagePre [8]edwards25519.CachedGroupElement
edwards25519.GePrecompute(&keyImagePre, keyImageGe)
toHash := prefixHash[:]
var one, tmpS, sum [32]byte
one[0] = 1
for i, pubKey := range pubKeys {
signature := ringSignature[i]
tmpE := new(edwards25519.ExtendedGroupElement)
tmpP := new(edwards25519.ProjectiveGroupElement)
pubKeyBytes := [32]byte(pubKey)
tmpE.FromBytes(&pubKeyBytes)
var tmpPBytes, tmpEBytes [32]byte
edwards25519.GeDoubleScalarMultVartime(tmpP, &signature.c, tmpE, &signature.r)
tmpP.ToBytes(&tmpPBytes)
toHash = append(toHash, tmpPBytes[:]...)
HashToEC(pubKey, tmpE)
tmpE.ToBytes(&tmpEBytes)
edwards25519.GeDoubleScalarMultPrecompVartime(tmpP, &signature.r, tmpE, &signature.c, &keyImagePre)
tmpP.ToBytes(&tmpPBytes)
toHash = append(toHash, tmpPBytes[:]...)
edwards25519.ScAdd(&sum, &sum, &signature.c)
}
tmpS = Keccak256(toHash)
fmt.Printf("%x\n%x\n", tmpS, sum)
edwards25519.ScSub(&sum, &tmpS, &sum)
result = edwards25519.ScIsZero(&sum)
return
}
/* Below are structs for the CT version */
type Key [32]byte
type ctKey struct {
@ -47,7 +165,7 @@ type RctSigPrunable struct {
MGs []mgSig
}
type RingSignature struct {
type RingSignatureCT struct {
RingSignatureBase
RctSigPrunable
}

76
ringsignature_test.go Normal file
View file

@ -0,0 +1,76 @@
package moneroutil
import (
"bytes"
"encoding/hex"
"testing"
)
func TestVerifySignature(t *testing.T) {
tests := []struct {
name string
prefixHashHex string
keyImageHex string
pubKeys []string
ringSignatureElements []string
}{
{
name: "from monero cryptotest.pl 1",
prefixHashHex: "8e41962058b7422e7404253121489a3e63d186ed115086919a75105661483ba9",
keyImageHex: "350b7ead2fc72a3c5ac7f864e6bed350d6c8ca8e56f98757c2a035e0eb67f71f",
pubKeys: []string{
"a403aa1c2dab5fe95e847d50a048025936aefb12b6af10a13d462bd792c93a51",
"664422cf6f4100dc6b3298e41ca53b173a98918fc9cb50fc2d590b7d1285f4ab",
"1eb0e3db58e142cf4add0062479aa32f643e9952938eccfb0f95a841c1539129",
},
ringSignatureElements: []string{
"a6a8f9248a30dca0253627e1ca1a207c48830668777fe8173aa7f8a074349204519d759714264bd129962b263a96c6a76ec9e76f9d6080180adb7b1d2f562d06",
"d3c9f830689ed3a611ac84fe19f8de70e50c2ddf1b1067b8495a3e046b104908b1bdb749c159cde2f808c39d62ccafc355f03207105c6a73ed3eaf66b8a29a09",
"9093e4f426b1a1360d4ade97dbdcf8f7745e6dbe9421be5a19b76a3ca511a907bc9b4379ce4168555e40e7f36211aab9f4ced5e0ace3c6845da2cb966d45d601",
},
},
{
name: "from monero cryptotest.pl 2",
prefixHashHex: "8a597f11961935e32e0adeab2ce48b3df2d907c9b26619dad22f42ff65ab7593",
keyImageHex: "fb99a056b4a74d4365f64dd124897c03e0c2d3920abad533de6b4e1878edbb36",
pubKeys: []string{
"6a7a81a52ba91b9785b484d761bfb3ad9a473c147e17b7fbbc3992e8c97108d7",
"0f3fe9c20b24a11bf4d6d1acd335c6a80543f1f0380590d7323caf1390c78e88",
},
ringSignatureElements: []string{
"2c15e4de88ff38d655e2deef0e06a7ca4541a7754c37e7b20875cce791754508b7903a4a3aca7253bb98be335014bebb33683aedca0bc46e288e229ecfccbe0e",
"026c8d9801f7330aa82426adf5bacf4546d83df0cc12321ede90df8c0d9aa8006acae497177b2eeaf658b813eaf50e1e06f3d1107694beff9b520c65ee624f05",
},
},
{
name: "1/17 inputs form block 40646",
prefixHashHex: "aeecb4170b276d2ac69a7abca86f82621f56d943c8d4a8900cd56192da8d442d",
keyImageHex: "c9679ba9ca8a6fa87a1352985e46ea3723489d3699ab1af075532f711739b9c5",
pubKeys: []string{
"6646f168c842275b31ca863f6eac8eed9e5dfc5714d5864efb62f6c340298a30",
},
ringSignatureElements: []string{
"11b4d1bd92e85f38152848cbf100c6f8b15c9de5278e4506bb9131230807d60e658188593715e7980a9d9e188d2114f2a3b71541cfe66fb94413237edf36dc0a",
},
},
}
for _, test := range tests {
prefixHash := Hash(HexToBytes(test.prefixHashHex))
keyImage := PubKey(HexToBytes(test.keyImageHex))
pubKeys := make([]PubKey, len(test.pubKeys))
for i, pubKeyHex := range test.pubKeys {
pubKeys[i] = PubKey(HexToBytes(pubKeyHex))
}
ringSignature := make([]*RingSignatureElement, len(test.ringSignatureElements))
for i, ringSignatureElementHex := range test.ringSignatureElements {
ringSignatureElementBytes, _ := hex.DecodeString(ringSignatureElementHex)
buffer := new(bytes.Buffer)
buffer.Write(ringSignatureElementBytes)
ringSignature[i], _ = ParseSignature(buffer)
}
if !VerifySignature(prefixHash, keyImage, pubKeys, ringSignature) {
t.Errorf("%s: signature not verified", test.name)
continue
}
}
}

View file

@ -14,18 +14,8 @@ const (
txOutToScriptMarker = 0
txOutToScriptHashMarker = 1
txOutToKeyMarker = 2
HashLength = 32
PubKeyLength = 32
)
type Hash [HashLength]byte
type PubKey [PubKeyLength]byte
type Signature struct {
c [PubKeyLength]byte
r [PubKeyLength]byte
}
type txOutToScript struct {
pubKeys []PubKey
script []byte
@ -87,7 +77,7 @@ type TransactionPrefix struct {
type Transaction struct {
TransactionPrefix
signatures [][]*Signature
signatures []RingSignature
}
func (h *Hash) Serialize() (result []byte) {
@ -100,13 +90,6 @@ func (p *PubKey) Serialize() (result []byte) {
return
}
func (s *Signature) Serialize() (result []byte) {
result = make([]byte, 64)
copy(result, s.c[:])
copy(result[32:64], s.r[:])
return
}
func (t *txOutToScript) TargetSerialize() (result []byte) {
result = []byte{txInToScriptMarker}
for i, pubkey := range t.pubKeys {
@ -210,6 +193,18 @@ func (t *TransactionPrefix) SerializePrefix() (result []byte) {
return
}
func (t *TransactionPrefix) PrefixHash() (hash Hash) {
hash = Keccak256(t.SerializePrefix())
return
}
func (t *TransactionPrefix) OutputSum() (sum uint64) {
for _, output := range t.vout {
sum += output.amount
}
return
}
func (t *Transaction) Serialize() (result []byte) {
result = t.SerializePrefix()
for i := 0; i < len(t.signatures); i++ {
@ -349,40 +344,6 @@ func ParseExtra(buf *bytes.Buffer) (extra []byte, err error) {
return
}
func ParseSignature(buf *bytes.Buffer) (signature *Signature, err error) {
s := new(Signature)
c := buf.Next(PubKeyLength)
if len(c) != PubKeyLength {
err = errors.New("Not enough bytes for signature c")
return
}
copy(s.c[:], c)
r := buf.Next(PubKeyLength)
if len(r) != PubKeyLength {
err = errors.New("Not enough bytes for signature r")
return
}
copy(s.r[:], r)
signature = s
return
}
func ParseSignatures(mixinLengths []int, buf *bytes.Buffer) (signatures [][]*Signature, err error) {
// mixinLengths is the number of mixins at each input position
sigs := make([][]*Signature, len(mixinLengths), len(mixinLengths))
for i, nMixin := range mixinLengths {
sigs[i] = make([]*Signature, nMixin, nMixin)
for j := 0; j < nMixin; j++ {
sigs[i][j], err = ParseSignature(buf)
if err != nil {
return
}
}
}
signatures = sigs
return
}
func ParseTransaction(buf *bytes.Buffer) (transaction *Transaction, err error) {
t := new(Transaction)
version, err := ReadVarInt(buf)
@ -436,10 +397,3 @@ func ParseTransaction(buf *bytes.Buffer) (transaction *Transaction, err error) {
transaction = t
return
}
func (t *TransactionPrefix) OutputSum() (sum uint64) {
for _, output := range t.vout {
sum += output.amount
}
return
}

View file

@ -98,7 +98,7 @@ func TestCoinbaseTransaction(t *testing.T) {
serializedTx, _ := hex.DecodeString(test.txHex)
expectedHash, _ := hex.DecodeString(test.hashHex)
hash := Keccak256(serializedTx)
if bytes.Compare(expectedHash, hash) != 0 {
if bytes.Compare(expectedHash, hash[:]) != 0 {
t.Errorf("%s: want %x, got %x", test.name, expectedHash, hash)
}
buffer := new(bytes.Buffer)
@ -270,7 +270,7 @@ func TestTxInToKeyTransaction(t *testing.T) {
serializedTx, _ := hex.DecodeString(test.txHex)
expectedHash, _ := hex.DecodeString(test.hashHex)
hash := Keccak256(serializedTx)
if bytes.Compare(expectedHash, hash) != 0 {
if bytes.Compare(expectedHash, hash[:]) != 0 {
t.Errorf("%s: want %x, got %x", test.name, expectedHash, hash)
}
buffer := new(bytes.Buffer)