You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

148 lines
4.6 KiB

  1. package common
  2. import (
  3. "time"
  4. ethCommon "github.com/ethereum/go-ethereum/common"
  5. ethMath "github.com/ethereum/go-ethereum/common/math"
  6. ethCrypto "github.com/ethereum/go-ethereum/crypto"
  7. ethSigner "github.com/ethereum/go-ethereum/signer/core"
  8. "github.com/hermeznetwork/tracerr"
  9. "github.com/iden3/go-iden3-crypto/babyjub"
  10. )
  11. const (
  12. // AccountCreationAuthMsg is the message that is signed to authorize a
  13. // Hermez account creation
  14. AccountCreationAuthMsg = "Account creation"
  15. // EIP712Version is the used version of the EIP-712
  16. EIP712Version = "1"
  17. // EIP712Provider defines the Provider for the EIP-712
  18. EIP712Provider = "Hermez Network"
  19. )
  20. var (
  21. // EmptyEthSignature is an ethereum signature of all zeroes
  22. EmptyEthSignature = make([]byte, 65)
  23. )
  24. // AccountCreationAuth authorizations sent by users to the L2DB, to be used for
  25. // account creations when necessary
  26. type AccountCreationAuth struct {
  27. EthAddr ethCommon.Address `meddler:"eth_addr"`
  28. BJJ babyjub.PublicKeyComp `meddler:"bjj"`
  29. Signature []byte `meddler:"signature"`
  30. Timestamp time.Time `meddler:"timestamp,utctime"`
  31. }
  32. // toHash returns a byte array to be hashed from the AccountCreationAuth, which
  33. // follows the EIP-712 encoding
  34. func (a *AccountCreationAuth) toHash(chainID uint16,
  35. hermezContractAddr ethCommon.Address) ([]byte, error) {
  36. chainIDFormatted := ethMath.NewHexOrDecimal256(int64(chainID))
  37. signerData := ethSigner.TypedData{
  38. Types: ethSigner.Types{
  39. "EIP712Domain": []ethSigner.Type{
  40. {Name: "name", Type: "string"},
  41. {Name: "version", Type: "string"},
  42. {Name: "chainId", Type: "uint256"},
  43. {Name: "verifyingContract", Type: "address"},
  44. },
  45. "Authorise": []ethSigner.Type{
  46. {Name: "Provider", Type: "string"},
  47. {Name: "Authorisation", Type: "string"},
  48. {Name: "BJJKey", Type: "bytes32"},
  49. },
  50. },
  51. PrimaryType: "Authorise",
  52. Domain: ethSigner.TypedDataDomain{
  53. Name: EIP712Provider,
  54. Version: EIP712Version,
  55. ChainId: chainIDFormatted,
  56. VerifyingContract: hermezContractAddr.Hex(),
  57. },
  58. Message: ethSigner.TypedDataMessage{
  59. "Provider": EIP712Provider,
  60. "Authorisation": AccountCreationAuthMsg,
  61. "BJJKey": SwapEndianness(a.BJJ[:]),
  62. },
  63. }
  64. domainSeparator, err := signerData.HashStruct("EIP712Domain", signerData.Domain.Map())
  65. if err != nil {
  66. return nil, tracerr.Wrap(err)
  67. }
  68. typedDataHash, err := signerData.HashStruct(signerData.PrimaryType, signerData.Message)
  69. if err != nil {
  70. return nil, tracerr.Wrap(err)
  71. }
  72. rawData := []byte{0x19, 0x01} // "\x19\x01"
  73. rawData = append(rawData, domainSeparator...)
  74. rawData = append(rawData, typedDataHash...)
  75. return rawData, nil
  76. }
  77. // HashToSign returns the hash to be signed by the Ethereum address to authorize
  78. // the account creation, which follows the EIP-712 encoding
  79. func (a *AccountCreationAuth) HashToSign(chainID uint16,
  80. hermezContractAddr ethCommon.Address) ([]byte, error) {
  81. b, err := a.toHash(chainID, hermezContractAddr)
  82. if err != nil {
  83. return nil, tracerr.Wrap(err)
  84. }
  85. return ethCrypto.Keccak256(b), nil
  86. }
  87. // Sign signs the account creation authorization message using the provided
  88. // `signHash` function, and stores the signature in `a.Signature`. `signHash`
  89. // should do an ethereum signature using the account corresponding to
  90. // `a.EthAddr`. The `signHash` function is used to make signing flexible: in
  91. // tests we sign directly using the private key, outside tests we sign using
  92. // the keystore (which never exposes the private key). Sign follows the EIP-712
  93. // encoding.
  94. func (a *AccountCreationAuth) Sign(signHash func(hash []byte) ([]byte, error),
  95. chainID uint16, hermezContractAddr ethCommon.Address) error {
  96. hash, err := a.HashToSign(chainID, hermezContractAddr)
  97. if err != nil {
  98. return tracerr.Wrap(err)
  99. }
  100. sig, err := signHash(hash)
  101. if err != nil {
  102. return tracerr.Wrap(err)
  103. }
  104. sig[64] += 27
  105. a.Signature = sig
  106. a.Timestamp = time.Now()
  107. return nil
  108. }
  109. // VerifySignature ensures that the Signature is done with the EthAddr, for the
  110. // chainID and hermezContractAddress passed by parameter. VerifySignature
  111. // follows the EIP-712 encoding.
  112. func (a *AccountCreationAuth) VerifySignature(chainID uint16,
  113. hermezContractAddr ethCommon.Address) bool {
  114. // Calculate hash to be signed
  115. hash, err := a.HashToSign(chainID, hermezContractAddr)
  116. if err != nil {
  117. return false
  118. }
  119. var sig [65]byte
  120. copy(sig[:], a.Signature[:])
  121. sig[64] -= 27
  122. // Get public key from Signature
  123. pubKBytes, err := ethCrypto.Ecrecover(hash, sig[:])
  124. if err != nil {
  125. return false
  126. }
  127. pubK, err := ethCrypto.UnmarshalPubkey(pubKBytes)
  128. if err != nil {
  129. return false
  130. }
  131. // Get addr from pubK
  132. addr := ethCrypto.PubkeyToAddress(*pubK)
  133. return addr == a.EthAddr
  134. }