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