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 }