git.gammaspectra.live/P2Pool/consensus@v0.0.0-20240403173234-a039820b20c9/monero/address/crypto.go (about)

     1  package address
     2  
     3  import (
     4  	"encoding/binary"
     5  	"git.gammaspectra.live/P2Pool/consensus/monero/crypto"
     6  	p2poolcrypto "git.gammaspectra.live/P2Pool/consensus/p2pool/crypto"
     7  	"git.gammaspectra.live/P2Pool/consensus/types"
     8  	"git.gammaspectra.live/P2Pool/edwards25519"
     9  	"git.gammaspectra.live/P2Pool/moneroutil"
    10  	"git.gammaspectra.live/P2Pool/sha3"
    11  	"strings"
    12  )
    13  
    14  // ZeroPrivateKeyAddress Special address with private keys set to both zero.
    15  // Useful to detect unsupported signatures from hardware wallets on Monero GUI
    16  var ZeroPrivateKeyAddress PackedAddress
    17  
    18  func init() {
    19  	ZeroPrivateKeyAddress[PackedAddressSpend] = crypto.ZeroPrivateKeyBytes.PublicKey().AsBytes()
    20  	ZeroPrivateKeyAddress[PackedAddressView] = crypto.ZeroPrivateKeyBytes.PublicKey().AsBytes()
    21  }
    22  
    23  func GetDeterministicTransactionPrivateKey(seed types.Hash, prevId types.Hash) crypto.PrivateKey {
    24  	return p2poolcrypto.GetDeterministicTransactionPrivateKey(seed, prevId)
    25  }
    26  
    27  func GetPublicKeyForSharedData(a Interface, sharedData crypto.PrivateKey) crypto.PublicKey {
    28  	return sharedData.PublicKey().AsPoint().Add(a.SpendPublicKey().AsPoint())
    29  }
    30  
    31  func GetEphemeralPublicKey(a Interface, txKey crypto.PrivateKey, outputIndex uint64) crypto.PublicKey {
    32  	return GetPublicKeyForSharedData(a, crypto.GetDerivationSharedDataForOutputIndex(txKey.GetDerivationCofactor(a.ViewPublicKey()), outputIndex))
    33  }
    34  
    35  func getEphemeralPublicKeyInline(spendPub, viewPub *edwards25519.Point, txKey *edwards25519.Scalar, outputIndex uint64, p *edwards25519.Point) {
    36  	//derivation
    37  	p.UnsafeVarTimeScalarMult(txKey, viewPub).MultByCofactor(p)
    38  
    39  	derivationAsBytes := p.Bytes()
    40  	var varIntBuf [binary.MaxVarintLen64]byte
    41  
    42  	sharedData := crypto.HashToScalarNoAllocate(derivationAsBytes, varIntBuf[:binary.PutUvarint(varIntBuf[:], outputIndex)])
    43  
    44  	//public key + add
    45  	p.ScalarBaseMult(&sharedData).Add(p, spendPub)
    46  }
    47  
    48  func GetEphemeralPublicKeyAndViewTag(a Interface, txKey crypto.PrivateKey, outputIndex uint64) (crypto.PublicKey, uint8) {
    49  	pK, viewTag := crypto.GetDerivationSharedDataAndViewTagForOutputIndex(txKey.GetDerivationCofactor(a.ViewPublicKey()), outputIndex)
    50  	return GetPublicKeyForSharedData(a, pK), viewTag
    51  }
    52  
    53  // GetEphemeralPublicKeyAndViewTagNoAllocate Special version of GetEphemeralPublicKeyAndViewTag
    54  func GetEphemeralPublicKeyAndViewTagNoAllocate(spendPublicKeyPoint *edwards25519.Point, derivation crypto.PublicKeyBytes, txKey *edwards25519.Scalar, outputIndex uint64, hasher *sha3.HasherState) (crypto.PublicKeyBytes, uint8) {
    55  	var intermediatePublicKey, ephemeralPublicKey edwards25519.Point
    56  	derivationSharedData, viewTag := crypto.GetDerivationSharedDataAndViewTagForOutputIndexNoAllocate(derivation, outputIndex, hasher)
    57  
    58  	intermediatePublicKey.ScalarBaseMult(&derivationSharedData)
    59  	ephemeralPublicKey.Add(&intermediatePublicKey, spendPublicKeyPoint)
    60  
    61  	var ephemeralPublicKeyBytes crypto.PublicKeyBytes
    62  	copy(ephemeralPublicKeyBytes[:], ephemeralPublicKey.Bytes())
    63  
    64  	return ephemeralPublicKeyBytes, viewTag
    65  }
    66  
    67  // GetDerivationNoAllocate Special version
    68  func GetDerivationNoAllocate(viewPublicKeyPoint *edwards25519.Point, txKey *edwards25519.Scalar) crypto.PublicKeyBytes {
    69  	var point, derivation edwards25519.Point
    70  	point.UnsafeVarTimeScalarMult(txKey, viewPublicKeyPoint)
    71  	derivation.MultByCofactor(&point)
    72  
    73  	return crypto.PublicKeyBytes(derivation.Bytes())
    74  }
    75  
    76  func GetTxProofV2(a Interface, txId types.Hash, txKey crypto.PrivateKey, message string) string {
    77  	prefixHash := crypto.Keccak256(txId[:], []byte(message))
    78  
    79  	sharedSecret, signature := crypto.GenerateTxProofV2(prefixHash, txKey, a.ViewPublicKey(), nil)
    80  
    81  	return "OutProofV2" + string(moneroutil.EncodeMoneroBase58(sharedSecret.AsSlice())) + string(moneroutil.EncodeMoneroBase58(signature.Bytes()))
    82  }
    83  
    84  func GetTxProofV1(a Interface, txId types.Hash, txKey crypto.PrivateKey, message string) string {
    85  	prefixHash := crypto.Keccak256(txId[:], []byte(message))
    86  
    87  	sharedSecret, signature := crypto.GenerateTxProofV1(prefixHash, txKey, a.ViewPublicKey(), nil)
    88  
    89  	return "OutProofV1" + string(moneroutil.EncodeMoneroBase58(sharedSecret.AsSlice())) + string(moneroutil.EncodeMoneroBase58(signature.Bytes()))
    90  }
    91  
    92  type SignatureVerifyResult int
    93  
    94  const (
    95  	ResultFailZeroSpend SignatureVerifyResult = -2
    96  	ResultFailZeroView  SignatureVerifyResult = -1
    97  )
    98  const (
    99  	ResultFail = SignatureVerifyResult(iota)
   100  	ResultSuccessSpend
   101  	ResultSuccessView
   102  )
   103  
   104  func GetMessageHash(a Interface, message []byte, mode uint8) types.Hash {
   105  	return crypto.Keccak256(
   106  		[]byte("MoneroMessageSignature\x00"),
   107  		a.SpendPublicKey().AsSlice(),
   108  		a.ViewPublicKey().AsSlice(),
   109  		[]byte{mode},
   110  		binary.AppendUvarint(nil, uint64(len(message))),
   111  		message,
   112  	)
   113  }
   114  
   115  func VerifyMessage(a Interface, message []byte, signature string) SignatureVerifyResult {
   116  	var hash types.Hash
   117  
   118  	if strings.HasPrefix(signature, "SigV1") {
   119  		hash = crypto.Keccak256(message)
   120  	} else if strings.HasPrefix(signature, "SigV2") {
   121  		hash = GetMessageHash(a, message, 0)
   122  	} else {
   123  		return ResultFail
   124  	}
   125  	raw := moneroutil.DecodeMoneroBase58([]byte(signature[5:]))
   126  
   127  	sig := crypto.NewSignatureFromBytes(raw)
   128  
   129  	if sig == nil {
   130  		return ResultFail
   131  	}
   132  
   133  	if crypto.VerifyMessageSignature(hash, a.SpendPublicKey(), sig) {
   134  		return ResultSuccessSpend
   135  	}
   136  
   137  	// Special mode: view wallets in Monero GUI could generate signatures with spend public key proper, with message hash of spend wallet mode, but zero spend private key
   138  	if crypto.VerifyMessageSignatureSplit(hash, a.SpendPublicKey(), ZeroPrivateKeyAddress.SpendPublicKey(), sig) {
   139  		return ResultFailZeroSpend
   140  	}
   141  
   142  	if strings.HasPrefix(signature, "SigV2") {
   143  		hash = GetMessageHash(a, message, 1)
   144  	}
   145  
   146  	if crypto.VerifyMessageSignature(hash, a.ViewPublicKey(), sig) {
   147  		return ResultSuccessView
   148  	}
   149  
   150  	return ResultFail
   151  }
   152  
   153  // VerifyMessageFallbackToZero Check for Monero GUI behavior to generate wrong signatures on view-only wallets
   154  func VerifyMessageFallbackToZero(a Interface, message []byte, signature string) SignatureVerifyResult {
   155  	var hash types.Hash
   156  
   157  	if strings.HasPrefix(signature, "SigV1") {
   158  		hash = crypto.Keccak256(message)
   159  	} else if strings.HasPrefix(signature, "SigV2") {
   160  		hash = GetMessageHash(a, message, 0)
   161  	} else {
   162  		return ResultFail
   163  	}
   164  	raw := moneroutil.DecodeMoneroBase58([]byte(signature[5:]))
   165  
   166  	sig := crypto.NewSignatureFromBytes(raw)
   167  
   168  	if sig == nil {
   169  		return ResultFail
   170  	}
   171  
   172  	if crypto.VerifyMessageSignature(hash, a.SpendPublicKey(), sig) {
   173  		return ResultSuccessSpend
   174  	}
   175  
   176  	// Special mode: view wallets in Monero GUI could generate signatures with spend public key proper, with message hash of spend wallet mode, but zero spend private key
   177  	if crypto.VerifyMessageSignatureSplit(hash, a.SpendPublicKey(), ZeroPrivateKeyAddress.SpendPublicKey(), sig) {
   178  		return ResultFailZeroSpend
   179  	}
   180  
   181  	if strings.HasPrefix(signature, "SigV2") {
   182  		hash = GetMessageHash(a, message, 1)
   183  	}
   184  
   185  	if crypto.VerifyMessageSignature(hash, a.ViewPublicKey(), sig) {
   186  		return ResultSuccessView
   187  	}
   188  
   189  	// Special mode
   190  	if crypto.VerifyMessageSignatureSplit(hash, a.ViewPublicKey(), ZeroPrivateKeyAddress.ViewPublicKey(), sig) {
   191  		return ResultFailZeroView
   192  	}
   193  
   194  	return ResultFail
   195  }