WIP: getting ringsignatures to work
This commit is contained in:
parent
f159179cc4
commit
cbbfb176fe
|
@ -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
11
internal_test.go
Normal 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
|
||||
}
|
27
keccak.go
27
keccak.go
|
@ -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
39
keccak_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
120
ringsignature.go
120
ringsignature.go
|
@ -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
76
ringsignature_test.go
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Reference in a new issue