Fix byte order for Urbit Ed25519 key compatibility

- Add reverseBytes helper function for key processing
- Reverse authentication and encryption key bytes when returned from GraphQL
- This resolves signature verification failures in downstream applications
- Urbit stores Ed25519 keys in reverse byte order compared to standard implementations

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
John Hyde 2025-09-09 10:22:19 -07:00
parent adf7a63f35
commit 60ee72d192

View File

@ -11,17 +11,28 @@ import (
"github.com/Khan/genqlient/graphql"
)
// reverseBytes reverses the byte order of the input slice
// This is needed because Urbit stores Ed25519 keys in reverse byte order
// compared to the standard Go crypto/ed25519 implementation
func reverseBytes(input []byte) []byte {
result := make([]byte, len(input))
for i, b := range input {
result[len(input)-1-i] = b
}
return result
}
// Default configuration constants
const (
// DefaultEndpoint is the default azimuth-watcher GraphQL endpoint
DefaultEndpoint = "https://azimuth.dev.vdb.to/graphql"
// AzimuthContract is the Azimuth contract address on Ethereum mainnet
AzimuthContract = "0x223c067F8CF28ae173EE5CafEa60cA44C335fecB"
// DefaultCacheTTL is the default cache duration for ship keys
DefaultCacheTTL = 1 * time.Hour
// DefaultTimeout is the default HTTP client timeout
DefaultTimeout = 30 * time.Second
)
@ -88,7 +99,7 @@ func NewClientWithOptions(opts ClientOptions) *Client {
}
httpClient := graphql.NewClient(opts.Endpoint, nil)
return &Client{
gqlClient: httpClient,
cache: NewKeyCache(opts.CacheTTL),
@ -319,7 +330,8 @@ func (c *Client) getShipKeys(ctx context.Context, point uint32) (*ShipKeys, erro
if err != nil {
return nil, fmt.Errorf("failed to decode encryption key: %w", err)
}
keys.EncryptionKey = encKey
// Reverse the authentication key bytes for Urbit Ed25519 compatibility
keys.EncryptionKey = reverseBytes(encKey)
}
// Parse authentication key (32 bytes hex, remove 0x prefix if present)
@ -334,7 +346,8 @@ func (c *Client) getShipKeys(ctx context.Context, point uint32) (*ShipKeys, erro
if len(authKey) != 32 {
return nil, fmt.Errorf("invalid authentication key length: expected 32 bytes, got %d", len(authKey))
}
keys.AuthenticationKey = authKey
// Reverse the authentication key bytes for Urbit Ed25519 compatibility
keys.AuthenticationKey = reverseBytes(authKey)
// Parse crypto suite version
suite, err := resp.AzimuthGetKeys.Value.Value2.ToUint32()
@ -372,7 +385,7 @@ func ValidateStarSponsorship(ctx context.Context, starID uint32, expectedGalaxyI
}
if sponsor != expectedGalaxyID {
return fmt.Errorf("star %d is not sponsored by galaxy %d (actual sponsor: %d)",
return fmt.Errorf("star %d is not sponsored by galaxy %d (actual sponsor: %d)",
starID, expectedGalaxyID, sponsor)
}
@ -385,11 +398,11 @@ func ParseStarID(starIDStr string) (uint32, error) {
if err != nil {
return 0, fmt.Errorf("invalid star ID format: %w", err)
}
// Validate that it's actually a star (256-65535)
if starPoint < 256 || starPoint > 65535 {
return 0, fmt.Errorf("ID %d is not a valid star (must be 256-65535)", starPoint)
}
return uint32(starPoint), nil
}
}