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
|
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
|
## Executables
|
||||||
|
|
||||||
The go-ethereum project comes with several wrappers/executables found in the `cmd`
|
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