mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-26 14:46:42 +00:00
cmd/clef: implement EIP-4361 SIWE (Sign-In With Ethereum) message validator
This commit is contained in:
parent
d0216db9d6
commit
e0498eb84b
4 changed files with 161 additions and 6 deletions
|
|
@ -29,12 +29,6 @@ or, to build the full suite of utilities:
|
|||
make all
|
||||
```
|
||||
|
||||
If make `geth fails` due to a missing `build/ci.go` file or related error, you can instead build Geth directly using:
|
||||
|
||||
```shell
|
||||
go build -o geth ./cmd/geth
|
||||
```
|
||||
|
||||
## Executables
|
||||
|
||||
The go-ethereum project comes with several wrappers/executables found in the `cmd`
|
||||
|
|
|
|||
75
cmd/clef/siwe-validator/README.md
Normal file
75
cmd/clef/siwe-validator/README.md
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
# SIWE Validator for Clef
|
||||
|
||||
This directory implements a minimal Sign-In with Ethereum (SIWE) message validator for Clef,
|
||||
designed to verify incoming EIP-4361 formatted messages before approving signing requests.
|
||||
|
||||
The validator checks critical fields including:
|
||||
|
||||
- **Domain**: Ensures the requested domain matches an expected domain (e.g., `localhost:3000`).
|
||||
- **Ethereum Address**: Verifies that a valid `0x` prefixed address is provided.
|
||||
- **URI**: Confirms the target URI matches the expected resource.
|
||||
- **Version**: Verifies that the SIWE version is `1`.
|
||||
- **ChainID**: Ensures the chain ID matches the intended network (e.g., `1` for Ethereum Mainnet).
|
||||
- **Nonce**: Checks that a unique nonce is included to prevent replay attacks.
|
||||
- **Issued At**: Ensures the issued timestamp follows the ISO 8601/RFC3339 format.
|
||||
|
||||
Unlike previous implementations relying on external libraries such as `spruceid/siwe-go`,
|
||||
this version introduces a **lightweight internal parser** that directly processes SIWE messages,
|
||||
eliminating external dependencies and improving maintainability.
|
||||
|
||||
---
|
||||
|
||||
## How It Works
|
||||
|
||||
Upon receiving a signing request, Clef will invoke the `siwe-validator` binary,
|
||||
passing the SIWE message via standard input (stdin).
|
||||
|
||||
The validator parses the message line-by-line and verifies mandatory fields according to EIP-4361 specifications.
|
||||
|
||||
If validation passes, Clef proceeds with the signing flow. Otherwise, signing is rejected.
|
||||
|
||||
### Manually Testing the Validator
|
||||
|
||||
You can manually simulate a Clef signing request by piping a SIWE message into `siwe-validator`.
|
||||
For example:
|
||||
|
||||
```bash
|
||||
echo "localhost:3000 wants you to sign in with your Ethereum account:
|
||||
0x32e0556aeC41a34C3002a264f4694193EBCf44F7
|
||||
|
||||
URI: https://localhost:3000
|
||||
Version: 1
|
||||
ChainID: 1
|
||||
Nonce: 32891756
|
||||
Issued At: 2025-04-26T12:00:00Z" | ./siwe-validator
|
||||
```
|
||||
|
||||
If the message is valid, `siwe-validator` will exit silently with code `0`.
|
||||
If the message is invalid, an error message will be printed to `stderr`.
|
||||
|
||||
---
|
||||
|
||||
## Test Data
|
||||
|
||||
The `testdata/genmsg_test.go` file provides a minimal static SIWE message generator for manual testing purposes.
|
||||
|
||||
It outputs a standardized EIP-4361 formatted message, allowing developers to easily validate the `siwe-validator` behavior.
|
||||
|
||||
This file is intended for manual verification only and is not part of the production codebase or automated tests.
|
||||
|
||||
To manually generate and test a SIWE message:
|
||||
|
||||
```bash
|
||||
cd cmd/clef/siwevalidator
|
||||
go run testdata/genmsg_test.go | ./siwe-validator
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- The validator currently supports basic field validation only.
|
||||
- Future improvements may include supporting optional fields like `Resources`, `Expiration Time`, and `Request ID`.
|
||||
- This implementation follows the EIP-4361.
|
||||
|
||||
---
|
||||
69
cmd/clef/siwe-validator/main.go
Normal file
69
cmd/clef/siwe-validator/main.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
raw, err := io.ReadAll(os.Stdin)
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, "failed to read stdin:", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
lines := strings.Split(string(raw), "\n")
|
||||
|
||||
if len(lines) < 8 {
|
||||
fmt.Fprintln(os.Stderr, "invalid message: not enough lines")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
domainLine := lines[0]
|
||||
addressLine := lines[1]
|
||||
uriLine := lines[3]
|
||||
versionLine := lines[4]
|
||||
chainIDLine := lines[5]
|
||||
nonceLine := lines[6]
|
||||
issuedAtLine := lines[7]
|
||||
|
||||
if !strings.Contains(domainLine, "localhost:3000") {
|
||||
fmt.Fprintf(os.Stderr, "domain mismatch: %s\n", domainLine)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(addressLine, "0x") {
|
||||
fmt.Fprintf(os.Stderr, "invalid address: %s\n", addressLine)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(uriLine, "URI: https://localhost:3000") {
|
||||
fmt.Fprintf(os.Stderr, "uri mismatch: %s\n", uriLine)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !strings.Contains(versionLine, "Version: 1") {
|
||||
fmt.Fprintf(os.Stderr, "version mismatch: %s\n", versionLine)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !strings.Contains(chainIDLine, "ChainID: 1") {
|
||||
fmt.Fprintf(os.Stderr, "chainID mismatch: %s\n", chainIDLine)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !strings.Contains(nonceLine, "Nonce:") {
|
||||
fmt.Fprintf(os.Stderr, "nonce missing: %s\n", nonceLine)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !strings.Contains(issuedAtLine, "Issued At:") {
|
||||
fmt.Fprintf(os.Stderr, "issued at missing: %s\n", issuedAtLine)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// 全部檢查通過
|
||||
os.Exit(0)
|
||||
}
|
||||
17
cmd/clef/siwe-validator/testdata/genmsg_test.go
vendored
Normal file
17
cmd/clef/siwe-validator/testdata/genmsg_test.go
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Print(`localhost:3000 wants you to sign in with your Ethereum account:
|
||||
0x32e0556aeC41a34C3002a264f4694193EBCf44F7
|
||||
|
||||
URI: https://localhost:3000
|
||||
Version: 1
|
||||
ChainID: 1
|
||||
Nonce: 32891756
|
||||
Issued At: 2025-04-26T12:00:00Z
|
||||
`)
|
||||
}
|
||||
Loading…
Reference in a new issue