2022-10-28 09:47:14 +00:00
package address
import (
"encoding/binary"
2023-05-20 08:49:05 +00:00
"git.gammaspectra.live/P2Pool/edwards25519"
2022-10-28 09:47:14 +00:00
"git.gammaspectra.live/P2Pool/moneroutil"
"git.gammaspectra.live/P2Pool/p2pool-observer/monero/crypto"
2022-11-03 11:32:07 +00:00
p2poolcrypto "git.gammaspectra.live/P2Pool/p2pool-observer/p2pool/crypto"
2022-10-28 09:47:14 +00:00
"git.gammaspectra.live/P2Pool/p2pool-observer/types"
2023-05-12 06:55:01 +00:00
"git.gammaspectra.live/P2Pool/sha3"
2022-10-28 09:47:14 +00:00
"strings"
)
2023-08-01 17:28:05 +00:00
// ZeroPrivateKeyAddress Special address with private keys set to both zero.
// Useful to detect unsupported signatures from hardware wallets on Monero GUI
var ZeroPrivateKeyAddress PackedAddress
func init ( ) {
ZeroPrivateKeyAddress [ PackedAddressSpend ] = crypto . ZeroPrivateKeyBytes . PublicKey ( ) . AsBytes ( )
ZeroPrivateKeyAddress [ PackedAddressView ] = crypto . ZeroPrivateKeyBytes . PublicKey ( ) . AsBytes ( )
}
2023-03-07 17:57:06 +00:00
func GetDeterministicTransactionPrivateKey ( seed types . Hash , prevId types . Hash ) crypto . PrivateKey {
return p2poolcrypto . GetDeterministicTransactionPrivateKey ( seed , prevId )
2022-10-28 09:47:14 +00:00
}
2022-11-05 05:29:12 +00:00
func GetPublicKeyForSharedData ( a Interface , sharedData crypto . PrivateKey ) crypto . PublicKey {
return sharedData . PublicKey ( ) . AsPoint ( ) . Add ( a . SpendPublicKey ( ) . AsPoint ( ) )
2022-10-28 09:47:14 +00:00
}
2022-11-05 05:29:12 +00:00
func GetEphemeralPublicKey ( a Interface , txKey crypto . PrivateKey , outputIndex uint64 ) crypto . PublicKey {
2022-11-07 08:52:35 +00:00
return GetPublicKeyForSharedData ( a , crypto . GetDerivationSharedDataForOutputIndex ( txKey . GetDerivationCofactor ( a . ViewPublicKey ( ) ) , outputIndex ) )
2022-10-28 09:47:14 +00:00
}
2022-11-07 22:59:52 +00:00
func getEphemeralPublicKeyInline ( spendPub , viewPub * edwards25519 . Point , txKey * edwards25519 . Scalar , outputIndex uint64 , p * edwards25519 . Point ) {
2022-11-07 14:58:02 +00:00
//derivation
2023-05-20 08:49:05 +00:00
p . UnsafeVarTimeScalarMult ( txKey , viewPub ) . MultByCofactor ( p )
2022-11-07 14:58:02 +00:00
derivationAsBytes := p . Bytes ( )
var varIntBuf [ binary . MaxVarintLen64 ] byte
2023-05-10 02:16:56 +00:00
sharedData := crypto . HashToScalarNoAllocate ( derivationAsBytes , varIntBuf [ : binary . PutUvarint ( varIntBuf [ : ] , outputIndex ) ] )
2022-11-07 14:58:02 +00:00
//public key + add
2023-05-10 02:16:56 +00:00
p . ScalarBaseMult ( & sharedData ) . Add ( p , spendPub )
2022-11-07 14:58:02 +00:00
}
func GetEphemeralPublicKeyAndViewTag ( a Interface , txKey crypto . PrivateKey , outputIndex uint64 ) ( crypto . PublicKey , uint8 ) {
2023-05-11 07:51:00 +00:00
pK , viewTag := crypto . GetDerivationSharedDataAndViewTagForOutputIndex ( txKey . GetDerivationCofactor ( a . ViewPublicKey ( ) ) , outputIndex )
return GetPublicKeyForSharedData ( a , pK ) , viewTag
2023-05-10 02:16:56 +00:00
}
2023-05-11 07:51:00 +00:00
// GetEphemeralPublicKeyAndViewTagNoAllocate Special version of GetEphemeralPublicKeyAndViewTag
2023-05-23 08:25:10 +00:00
func GetEphemeralPublicKeyAndViewTagNoAllocate ( spendPublicKeyPoint * edwards25519 . Point , derivation crypto . PublicKeyBytes , txKey * edwards25519 . Scalar , outputIndex uint64 , hasher * sha3 . HasherState ) ( crypto . PublicKeyBytes , uint8 ) {
var intermediatePublicKey , ephemeralPublicKey edwards25519 . Point
derivationSharedData , viewTag := crypto . GetDerivationSharedDataAndViewTagForOutputIndexNoAllocate ( derivation , outputIndex , hasher )
2023-05-11 07:51:00 +00:00
2023-05-23 07:31:20 +00:00
intermediatePublicKey . ScalarBaseMult ( & derivationSharedData )
ephemeralPublicKey . Add ( & intermediatePublicKey , spendPublicKeyPoint )
2023-05-11 07:51:00 +00:00
var ephemeralPublicKeyBytes crypto . PublicKeyBytes
copy ( ephemeralPublicKeyBytes [ : ] , ephemeralPublicKey . Bytes ( ) )
return ephemeralPublicKeyBytes , viewTag
2022-11-07 14:58:02 +00:00
}
2023-05-23 08:25:10 +00:00
// GetDerivationNoAllocate Special version
func GetDerivationNoAllocate ( viewPublicKeyPoint * edwards25519 . Point , txKey * edwards25519 . Scalar ) crypto . PublicKeyBytes {
var point , derivation edwards25519 . Point
point . UnsafeVarTimeScalarMult ( txKey , viewPublicKeyPoint )
derivation . MultByCofactor ( & point )
return crypto . PublicKeyBytes ( derivation . Bytes ( ) )
}
2022-11-05 05:29:12 +00:00
func GetTxProofV2 ( a Interface , txId types . Hash , txKey crypto . PrivateKey , message string ) string {
2022-11-07 22:59:52 +00:00
prefixHash := crypto . Keccak256 ( txId [ : ] , [ ] byte ( message ) )
2022-10-28 09:47:14 +00:00
2022-11-05 05:29:12 +00:00
sharedSecret , signature := crypto . GenerateTxProofV2 ( prefixHash , txKey , a . ViewPublicKey ( ) , nil )
2022-10-28 09:47:14 +00:00
2023-07-22 22:18:04 +00:00
return "OutProofV2" + string ( moneroutil . EncodeMoneroBase58 ( sharedSecret . AsSlice ( ) ) ) + string ( moneroutil . EncodeMoneroBase58 ( signature . Bytes ( ) ) )
2022-10-28 09:47:14 +00:00
}
2022-11-05 05:29:12 +00:00
func GetTxProofV1 ( a Interface , txId types . Hash , txKey crypto . PrivateKey , message string ) string {
2022-11-07 22:59:52 +00:00
prefixHash := crypto . Keccak256 ( txId [ : ] , [ ] byte ( message ) )
2022-10-28 09:47:14 +00:00
2022-11-05 05:29:12 +00:00
sharedSecret , signature := crypto . GenerateTxProofV1 ( prefixHash , txKey , a . ViewPublicKey ( ) , nil )
2022-10-28 09:47:14 +00:00
2023-07-22 22:18:04 +00:00
return "OutProofV1" + string ( moneroutil . EncodeMoneroBase58 ( sharedSecret . AsSlice ( ) ) ) + string ( moneroutil . EncodeMoneroBase58 ( signature . Bytes ( ) ) )
2022-10-28 09:47:14 +00:00
}
type SignatureVerifyResult int
const (
2023-08-02 18:48:39 +00:00
ResultFailZeroSpend SignatureVerifyResult = - 2
ResultFailZeroView SignatureVerifyResult = - 1
)
const (
ResultFail = SignatureVerifyResult ( iota )
2022-10-28 09:47:14 +00:00
ResultSuccessSpend
ResultSuccessView
)
2022-11-05 05:29:12 +00:00
func GetMessageHash ( a Interface , message [ ] byte , mode uint8 ) types . Hash {
2022-11-07 22:59:52 +00:00
return crypto . Keccak256 (
2022-11-05 08:33:53 +00:00
[ ] byte ( "MoneroMessageSignature\x00" ) ,
a . SpendPublicKey ( ) . AsSlice ( ) ,
a . ViewPublicKey ( ) . AsSlice ( ) ,
[ ] byte { mode } ,
binary . AppendUvarint ( nil , uint64 ( len ( message ) ) ) ,
message ,
2022-11-07 22:59:52 +00:00
)
2022-10-28 09:47:14 +00:00
}
2022-11-05 08:33:53 +00:00
func VerifyMessage ( a Interface , message [ ] byte , signature string ) SignatureVerifyResult {
2022-10-28 09:47:14 +00:00
var hash types . Hash
if strings . HasPrefix ( signature , "SigV1" ) {
2022-11-07 22:59:52 +00:00
hash = crypto . Keccak256 ( message )
2022-10-28 09:47:14 +00:00
} else if strings . HasPrefix ( signature , "SigV2" ) {
2022-11-05 05:29:12 +00:00
hash = GetMessageHash ( a , message , 0 )
2022-10-28 09:47:14 +00:00
} else {
return ResultFail
}
2023-07-22 22:18:04 +00:00
raw := moneroutil . DecodeMoneroBase58 ( [ ] byte ( signature [ 5 : ] ) )
2022-10-28 09:47:14 +00:00
sig := crypto . NewSignatureFromBytes ( raw )
if sig == nil {
return ResultFail
}
2022-11-05 05:29:12 +00:00
if crypto . VerifyMessageSignature ( hash , a . SpendPublicKey ( ) , sig ) {
2022-10-28 09:47:14 +00:00
return ResultSuccessSpend
}
2023-08-02 18:48:39 +00:00
// 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
if crypto . VerifyMessageSignatureSplit ( hash , a . SpendPublicKey ( ) , ZeroPrivateKeyAddress . SpendPublicKey ( ) , sig ) {
return ResultFailZeroSpend
}
if strings . HasPrefix ( signature , "SigV2" ) {
hash = GetMessageHash ( a , message , 1 )
}
if crypto . VerifyMessageSignature ( hash , a . ViewPublicKey ( ) , sig ) {
return ResultSuccessView
}
return ResultFail
}
// VerifyMessageFallbackToZero Check for Monero GUI behavior to generate wrong signatures on view-only wallets
func VerifyMessageFallbackToZero ( a Interface , message [ ] byte , signature string ) SignatureVerifyResult {
var hash types . Hash
if strings . HasPrefix ( signature , "SigV1" ) {
hash = crypto . Keccak256 ( message )
} else if strings . HasPrefix ( signature , "SigV2" ) {
hash = GetMessageHash ( a , message , 0 )
} else {
return ResultFail
}
raw := moneroutil . DecodeMoneroBase58 ( [ ] byte ( signature [ 5 : ] ) )
sig := crypto . NewSignatureFromBytes ( raw )
if sig == nil {
return ResultFail
}
if crypto . VerifyMessageSignature ( hash , a . SpendPublicKey ( ) , sig ) {
return ResultSuccessSpend
}
// 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
if crypto . VerifyMessageSignatureSplit ( hash , a . SpendPublicKey ( ) , ZeroPrivateKeyAddress . SpendPublicKey ( ) , sig ) {
return ResultFailZeroSpend
}
2022-10-28 09:47:14 +00:00
if strings . HasPrefix ( signature , "SigV2" ) {
2022-11-05 05:29:12 +00:00
hash = GetMessageHash ( a , message , 1 )
2022-10-28 09:47:14 +00:00
}
2022-11-05 05:29:12 +00:00
if crypto . VerifyMessageSignature ( hash , a . ViewPublicKey ( ) , sig ) {
2022-10-28 09:47:14 +00:00
return ResultSuccessView
}
2023-08-02 18:48:39 +00:00
// Special mode
if crypto . VerifyMessageSignatureSplit ( hash , a . ViewPublicKey ( ) , ZeroPrivateKeyAddress . ViewPublicKey ( ) , sig ) {
return ResultFailZeroView
}
2022-10-28 09:47:14 +00:00
return ResultFail
}