Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate new randomness r for payload, distinct from amount #179

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 35 additions & 4 deletions cryptography/crypto/userdata.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,13 @@

package crypto

import "math/big"
import "golang.org/x/crypto/chacha20"
import "github.com/deroproject/derohe/cryptography/bn256"
import (
"math/big"

import "github.com/go-logr/logr"
"github.com/deroproject/derohe/cryptography/bn256"
"github.com/go-logr/logr"
"golang.org/x/crypto/chacha20"
)

var Logger logr.Logger = logr.Discard() // default discard all logs, someone needs to set this up

Expand Down Expand Up @@ -54,3 +56,32 @@ func GenerateSharedSecret(secret *big.Int, peer_publickey *bn256.G1) (shared_key

return
}

// Derive a key from a random scalar
// We multiply the generator point by the scalar and hash the compressed point
func DeriveKeyFromR(r *BNRed) (key [32]byte) {
compressed := GPoint.ScalarMult(r).EncodeCompressed()
if len(compressed) != 33 {
panic("point compression needs to be fixed")
}

key = Keccak256(compressed[:])

return
}

// Derive a key from a point and a secret
// We multiply the point by the inverse of the secret and hash the compressed point
func DeriveKeyFromPoint(point *bn256.G1, secret *big.Int) (key [32]byte) {
sk_inversed := new(big.Int).ModInverse(secret, bn256.Order)

decrypted_point := new(bn256.G1).ScalarMult(point, sk_inversed)
compressed := decrypted_point.EncodeCompressed()
if len(compressed) != 33 {
panic("point compression needs to be fixed")
}

key = Keccak256(compressed[:])

return
}
42 changes: 42 additions & 0 deletions cryptography/crypto/userdata_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package crypto

import (
"testing"

"github.com/deroproject/derohe/cryptography/bn256"
)

func TestEncryptDecrypt(t *testing.T) {
var key [32]byte
expected := []byte("Hello World")
var payload []byte
payload = append(payload, expected...)

EncryptDecryptUserData(key, payload)
EncryptDecryptUserData(key, payload)

for i := range expected {
if expected[i] != payload[i] {
t.Fatal("error on encrypt/decrypt")
}
}
}

func TestSharedKey(t *testing.T) {
r := RandomScalarBNRed()
private_key := RandomScalarBNRed()
public_key := GPoint.ScalarMult(private_key)

// Create a curve point using PK
encrypted_key := new(bn256.G1).ScalarMult(public_key.G1(), r.BigInt())

// Decrypt it
decrypted_r := DeriveKeyFromPoint(encrypted_key, private_key.BigInt())

r_bytes := DeriveKeyFromR(r)
for i := range r_bytes {
if r_bytes[i] != decrypted_r[i] {
t.Fatalf("error on shared key, index: %d", i)
}
}
}
37 changes: 23 additions & 14 deletions proof/proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,17 @@

package proof

import "fmt"
import "math/big"
import "strings"
import "encoding/hex"

import "github.com/deroproject/derohe/cryptography/crypto"
import "github.com/deroproject/derohe/rpc"
import "github.com/deroproject/derohe/cryptography/bn256"
import "github.com/deroproject/derohe/transaction"
import (
"encoding/hex"
"fmt"
"math/big"
"strings"

"github.com/deroproject/derohe/cryptography/bn256"
"github.com/deroproject/derohe/cryptography/crypto"
"github.com/deroproject/derohe/rpc"
"github.com/deroproject/derohe/transaction"
)

//import "github.com/deroproject/derosuite/walletapi" // to decode encrypted payment ID

Expand Down Expand Up @@ -98,13 +100,20 @@ func Prove(proof string, input_tx string, ring_string [][]string, mainnet bool)
receivers = append(receivers, astring.String())
amounts = append(amounts, amount)

//crypto.EncryptDecryptUserData(addr.PublicKey.G1(), tx.Payloads[t].RPCPayload)
crypto.EncryptDecryptUserData(crypto.Keccak256(shared_key[:], tx.Payloads[t].Statement.Publickeylist[k].EncodeCompressed()), tx.Payloads[t].RPCPayload)
// skip first byte as it is not guaranteed, even rest of the bytes are not
payload := tx.Payloads[t].RPCPayload
switch tx.Payloads[t].RPCType {
case transaction.ENCRYPTED_DEFAULT_PAYLOAD_CBOR:
crypto.EncryptDecryptUserData(crypto.Keccak256(shared_key[:], tx.Payloads[t].Statement.Publickeylist[k].EncodeCompressed()), payload)
case transaction.ENCRYPTED_DEFAULT_PAYLOAD_CBOR_V2:
// Skip first 66 bytes as they are encrypted keys for both parties
payload = payload[66:]
crypto.EncryptDecryptUserData(crypto.Keccak256(shared_key[:], tx.Payloads[t].Statement.Publickeylist[k].EncodeCompressed()), payload)
}

payload_raw = append(payload_raw, tx.Payloads[t].RPCPayload[1:])
// skip first byte as it is not guaranteed, even rest of the bytes are not
payload_raw = append(payload_raw, payload[1:])
var args rpc.Arguments
if err := args.UnmarshalBinary(tx.Payloads[t].RPCPayload[1:]); err == nil {
if err := args.UnmarshalBinary(payload[1:]); err == nil {
payload_decoded = append(payload_decoded, fmt.Sprintf("%s", args))
} else {
payload_decoded = append(payload_decoded, err.Error())
Expand Down
25 changes: 17 additions & 8 deletions transaction/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@

package transaction

import "fmt"
import "bytes"
import "math/big"
import "encoding/binary"

import "github.com/deroproject/derohe/cryptography/crypto"
import "github.com/deroproject/derohe/cryptography/bn256"
import "github.com/deroproject/derohe/rpc"
import (
"bytes"
"encoding/binary"
"fmt"
"math/big"

"github.com/deroproject/derohe/cryptography/bn256"
"github.com/deroproject/derohe/cryptography/crypto"
"github.com/deroproject/derohe/rpc"
)

type TransactionType uint64

Expand Down Expand Up @@ -62,8 +64,15 @@ const PAYLOAD_LIMIT = 1 + 144 // entire payload header is mandatorily encrypted
// 144 byte payload ( to implement specific functionality such as delivery of keys etc), user dependent encryption
const PAYLOAD0_LIMIT = 144 // 1 byte has been reserved for sender position in ring representation in a byte, uptp 256 ring

// Payload V2 limit size
// 33 bytes are for the two keys included in the payload for decrypt/encrypt process
const PAYLOAD_V2_LIMIT = PAYLOAD0_LIMIT - (33 * 2)

const ENCRYPTED_DEFAULT_PAYLOAD_CBOR = byte(0)

// New payload format used
const ENCRYPTED_DEFAULT_PAYLOAD_CBOR_V2 = byte(1)

// the core transaction
// in our design, tx cam be sent by 1 wallet, but SC part/gas can be signed by any other user, but this is not implemented
type Transaction_Prefix struct {
Expand Down
81 changes: 70 additions & 11 deletions walletapi/daemon_communication.go
Original file line number Diff line number Diff line change
Expand Up @@ -922,18 +922,17 @@ func (w *Wallet_Memory) synchistory_block(scid crypto.Hash, topo int64) (err err
x.Add(new(bn256.G1).Set(&x), tx.Payloads[t].Statement.C[k]) // get the blinder
blinder := &x

shared_key := crypto.GenerateSharedSecret(r, tx.Payloads[t].Statement.Publickeylist[k])

// proof is blinder + amount transferred, it will recover the encrypted rpc payload also
// enable sender side proofs
proof := rpc.NewAddressFromKeys((*crypto.Point)(blinder))
proof.Proof = true
proof.Arguments = rpc.Arguments{{Name: "H", DataType: rpc.DataHash, Value: crypto.Hash(shared_key)}, {Name: rpc.RPC_VALUE_TRANSFER, DataType: rpc.DataUint64, Value: uint64(entry.Amount - entry.Burn)}}
entry.Proof = proof.String()

entry.PayloadType = tx.Payloads[t].RPCType
switch tx.Payloads[t].RPCType {

case transaction.ENCRYPTED_DEFAULT_PAYLOAD_CBOR:
shared_key := crypto.GenerateSharedSecret(r, tx.Payloads[t].Statement.Publickeylist[k])
proof.Arguments = rpc.Arguments{{Name: "H", DataType: rpc.DataHash, Value: crypto.Hash(shared_key)}, {Name: rpc.RPC_VALUE_TRANSFER, DataType: rpc.DataUint64, Value: uint64(entry.Amount - entry.Burn)}}

crypto.EncryptDecryptUserData(crypto.Keccak256(shared_key[:], tx.Payloads[t].Statement.Publickeylist[k].EncodeCompressed()), tx.Payloads[t].RPCPayload)
//fmt.Printf("decoded plaintext payload t %d %x\n",t,tx.Payloads[t].RPCPayload)
Expand All @@ -949,13 +948,39 @@ func (w *Wallet_Memory) synchistory_block(scid crypto.Hash, topo int64) (err err
args, _ := entry.ProcessPayload()
_ = args

// fmt.Printf("data received %s idx %d arguments %s\n", string(entry.Payload), sender_idx, args)
case transaction.ENCRYPTED_DEFAULT_PAYLOAD_CBOR_V2:
rpc_payload := tx.Payloads[t].RPCPayload
// We are the sender
var handle bn256.G1
err = handle.DecodeCompressed(rpc_payload[0:33])
if err != nil {
continue
}

r := crypto.DeriveKeyFromPoint(&handle, w.Get_Keys().Secret.BigInt())
key := crypto.Keccak256(r[:], tx.Payloads[t].Statement.Publickeylist[k].EncodeCompressed())

proof.Arguments = rpc.Arguments{{Name: "H", DataType: rpc.DataHash, Value: crypto.Hash(key)}, {Name: rpc.RPC_VALUE_TRANSFER, DataType: rpc.DataUint64, Value: uint64(entry.Amount - entry.Burn)}}

payload := rpc_payload[66:]
crypto.EncryptDecryptUserData(key, payload)

addr := rpc.NewAddressFromKeys((*crypto.Point)(w.account.Keys.Public.G1()))
addr.Mainnet = w.GetNetwork()
entry.Sender = addr.String()

entry.Payload = append(entry.Payload, rpc_payload[1:]...)
entry.Data = append(entry.Data, rpc_payload[:]...)

args, _ := entry.ProcessPayload()
_ = args

default:
entry.PayloadError = fmt.Sprintf("unknown payload type %d", tx.Payloads[t].RPCType)
entry.Payload = tx.Payloads[t].RPCPayload
}

entry.Proof = proof.String()
//paymentID := binary.BigEndian.Uint64(payment_id_encrypted_bytes[:]) // get decrypted payment id

addr := rpc.NewAddressFromKeys((*crypto.Point)(tx.Payloads[t].Statement.Publickeylist[k]))
Expand All @@ -981,18 +1006,16 @@ func (w *Wallet_Memory) synchistory_block(scid crypto.Hash, topo int64) (err err

blinder := &x

shared_key := crypto.GenerateSharedSecret(w.account.Keys.Secret.BigInt(), tx.Payloads[t].Statement.D)

// enable receiver side proofs
proof := rpc.NewAddressFromKeys((*crypto.Point)(blinder))
proof.Proof = true
proof.Arguments = rpc.Arguments{{Name: "H", DataType: rpc.DataHash, Value: crypto.Hash(shared_key)}, {Name: rpc.RPC_VALUE_TRANSFER, DataType: rpc.DataUint64, Value: uint64(entry.Amount)}}
entry.Proof = proof.String()

entry.PayloadType = tx.Payloads[t].RPCType
switch tx.Payloads[t].RPCType {

case 0:
case transaction.ENCRYPTED_DEFAULT_PAYLOAD_CBOR:
shared_key := crypto.GenerateSharedSecret(w.account.Keys.Secret.BigInt(), tx.Payloads[t].Statement.D)
proof.Arguments = rpc.Arguments{{Name: "H", DataType: rpc.DataHash, Value: crypto.Hash(shared_key)}, {Name: rpc.RPC_VALUE_TRANSFER, DataType: rpc.DataUint64, Value: uint64(entry.Amount)}}

//fmt.Printf("decoding encrypted payload %x\n",tx.Payloads[t].RPCPayload)
crypto.EncryptDecryptUserData(crypto.Keccak256(shared_key[:], w.GetAddress().PublicKey.EncodeCompressed()), tx.Payloads[t].RPCPayload)
Expand Down Expand Up @@ -1020,11 +1043,47 @@ func (w *Wallet_Memory) synchistory_block(scid crypto.Hash, topo int64) (err err

// fmt.Printf("data received %s idx %d arguments %s\n", string(entry.Payload), sender_idx, args)

case transaction.ENCRYPTED_DEFAULT_PAYLOAD_CBOR_V2:
rpc_payload := tx.Payloads[t].RPCPayload
// We are the receiver
var handle bn256.G1
err = handle.DecodeCompressed(rpc_payload[33:66])
if err != nil {
continue
}

r := crypto.DeriveKeyFromPoint(&handle, w.Get_Keys().Secret.BigInt())
key := crypto.Keccak256(r[:], w.GetAddress().PublicKey.EncodeCompressed())
proof.Arguments = rpc.Arguments{{Name: "H", DataType: rpc.DataHash, Value: crypto.Hash(key)}, {Name: rpc.RPC_VALUE_TRANSFER, DataType: rpc.DataUint64, Value: uint64(entry.Amount)}}

payload := rpc_payload[66:]
crypto.EncryptDecryptUserData(key, payload)
sender_idx := uint(payload[0])
// if ring size is 2, the other party is the sender so mark it so
if uint(tx.Payloads[t].Statement.RingSize) == 2 {
sender_idx = 0
if j == 0 {
sender_idx = 1
}
}

if sender_idx <= uint(tx.Payloads[t].Statement.RingSize) {
addr := rpc.NewAddressFromKeys((*crypto.Point)(tx.Payloads[t].Statement.Publickeylist[sender_idx]))
addr.Mainnet = w.GetNetwork()
entry.Sender = addr.String()
}

entry.Payload = append(entry.Payload, payload[1:]...)
entry.Data = append(entry.Data, payload[:]...)

args, _ := entry.ProcessPayload()
_ = args

default:
entry.PayloadError = fmt.Sprintf("unknown payload type %d", tx.Payloads[t].RPCType)
entry.Payload = tx.Payloads[t].RPCPayload
}

entry.Proof = proof.String()
//fmt.Printf("Received %s amount in TX(%d) %s payment id %x Src_ID %s data %s\n", globals.FormatMoney(changed_balance-previous_balance), tx.Height, bl.Tx_hashes[i].String(), entry.PaymentID, tx.Src_ID, tx.Data)
//fmt.Printf("Received amount in TX(%d) %s payment id %x Src_ID %s data %s\n", tx.Height, bl.Tx_hashes[i].String(), entry.PaymentID, tx.SrcID, tx.Data)
total_received += (changed_balance - previous_balance)
Expand Down
37 changes: 26 additions & 11 deletions walletapi/transaction_build.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ rebuild_tx:
panic("currently we cannot use more than 240 bits")
}

for t, _ := range transfers {
for t := range transfers {

var publickeylist, C, CLn, CRn []*bn256.G1
var D bn256.G1
Expand Down Expand Up @@ -148,6 +148,9 @@ rebuild_tx:
fees_done = true
}

// Generate a new randomness for the payload
r_payload := crypto.RandomScalarBNRed()

for i := range publickeylist { // setup commitments
var x bn256.G1
switch {
Expand All @@ -172,24 +175,36 @@ rebuild_tx:
panic("currently we donot support ring size >= 512")
}

asset.RPCType = transaction.ENCRYPTED_DEFAULT_PAYLOAD_CBOR
asset.RPCType = transaction.ENCRYPTED_DEFAULT_PAYLOAD_CBOR_V2

data, _ := transfers[t].Payload_RPC.CheckPack(transaction.PAYLOAD0_LIMIT)
data, err := transfers[t].Payload_RPC.CheckPack(transaction.PAYLOAD_V2_LIMIT)
if err != nil {
// TODO, we should returns all the error
return nil
}

shared_key := crypto.GenerateSharedSecret(r, publickeylist[i])
// Generate the keys for each party
// those are stored in the payload too
sender_key := new(bn256.G1).ScalarMult(publickeylist[witness_index[0]], r_payload.BigInt())
receiver_key := new(bn256.G1).ScalarMult(publickeylist[i], r_payload.BigInt())

// witness_index[0] is sender, witness_index[1] is receiver
asset.RPCPayload = append([]byte{byte(uint(witness_index[0]))}, data...)
// Each point is 33 bytes compressed
// This means we have a 66 bytes overhead
asset.RPCPayload = append(asset.RPCPayload, sender_key.EncodeCompressed()...)
asset.RPCPayload = append(asset.RPCPayload, receiver_key.EncodeCompressed()...)

//fmt.Printf("buulding shared_key %x index of receiver %d\n",shared_key,i)
//fmt.Printf("building plaintext payload %x\n",asset.RPCPayload)
var payload []byte
// witness_index[0] is sender, witness_index[1] is receiver
payload = append(payload, byte(uint(witness_index[0])))
payload = append(payload, data...)

//fmt.Printf("%d packed rpc payload %d %x\n ", t, len(data), data)
// make sure used data encryption is optional, just in case we would like to play together with ring members
// we intoduce an element to create dependency of input key, so receiver cannot prove otherwise
crypto.EncryptDecryptUserData(crypto.Keccak256(shared_key[:], publickeylist[i].EncodeCompressed()), asset.RPCPayload)
bytes := crypto.DeriveKeyFromR(r_payload)
crypto.EncryptDecryptUserData(crypto.Keccak256(bytes[:], publickeylist[i].EncodeCompressed()), payload)

//fmt.Printf("building encrypted payload %x\n",asset.RPCPayload)
// Inject the encrypted payload now
asset.RPCPayload = append(asset.RPCPayload, payload...)

default:
x.ScalarMult(crypto.G, new(big.Int).SetInt64(0))
Expand Down