Files
Olares/cli/pkg/wizard/did_key_utils.go
eball 99176209ea cli: add user activation command (#1972)
* feat: implement SSI Authentication Client and User Store

* feat: add user activation command with validation and wizard integration

* fix: set default values for BflUrl and VaultUrl in user activation command
2025-10-23 11:03:19 +08:00

204 lines
5.3 KiB
Go

package wizard
import (
"crypto/ed25519"
"crypto/hmac"
"crypto/sha512"
"encoding/base64"
"fmt"
"github.com/beclab/Olares/cli/pkg/web5/jwk"
"github.com/mr-tron/base58"
"github.com/multiformats/go-varint"
"github.com/tyler-smith/go-bip39"
)
// Ed25519 multicodec identifier
const ED25519_CODEC_ID = 0xed
// DIDKeyResult represents the result of DID key generation
type DIDKeyResult struct {
DID string `json:"did"`
PublicJWK jwk.JWK `json:"publicJwk"`
PrivateJWK jwk.JWK `json:"privateJwk"`
}
// HDWalletGo is a pure Go HD wallet based on Trust Wallet Core implementation
type HDWalletGo struct {
seed []byte
mnemonic string
passphrase string
}
// HDNode represents BIP32 hierarchical deterministic node
type HDNode struct {
privateKey []byte
publicKey []byte
chainCode []byte
depth uint8
childNum uint32
fingerprint uint32
}
// NewHDWalletFromMnemonic creates HD wallet from mnemonic (simulates Trust Wallet Core implementation)
func NewHDWalletFromMnemonic(mnemonic, passphrase string) (*HDWalletGo, error) {
// Validate mnemonic
if !bip39.IsMnemonicValid(mnemonic) {
return nil, fmt.Errorf("invalid mnemonic")
}
// Generate seed (64 bytes) - using standard BIP39 implementation here
seed := bip39.NewSeed(mnemonic, passphrase)
return &HDWalletGo{
seed: seed,
mnemonic: mnemonic,
passphrase: passphrase,
}, nil
}
// getMasterNode generates master node from seed (simulates Trust Wallet Core's hdnode_from_seed)
func (w *HDWalletGo) getMasterNode() (*HDNode, error) {
// BIP32 master key generation
// Use "ed25519 seed" as HMAC-SHA512 key
h := hmac.New(sha512.New, []byte("ed25519 seed"))
h.Write(w.seed)
hash := h.Sum(nil)
// First 32 bytes as private key, last 32 bytes as chain code
privateKey := hash[:32]
chainCode := hash[32:]
// Generate public key
publicKey := ed25519.NewKeyFromSeed(privateKey).Public().(ed25519.PublicKey)
return &HDNode{
privateKey: privateKey,
publicKey: publicKey,
chainCode: chainCode,
depth: 0,
childNum: 0,
fingerprint: 0,
}, nil
}
// GetMasterKeyEd25519 gets Ed25519 master key (simulates Trust Wallet Core's getMasterKey)
func (w *HDWalletGo) GetMasterKeyEd25519() (ed25519.PrivateKey, ed25519.PublicKey, error) {
node, err := w.getMasterNode()
if err != nil {
return nil, nil, err
}
// Generate complete Ed25519 private key from private key seed
privateKey := ed25519.NewKeyFromSeed(node.privateKey)
publicKey := privateKey.Public().(ed25519.PublicKey)
return privateKey, publicKey, nil
}
// GetPrivateJWKTrustWalletCore generates private JWK using Trust Wallet Core compatible method
func (w *HDWalletGo) GetPrivateJWKTrustWalletCore() (*DIDKeyResult, error) {
// Get Ed25519 key pair
privateKey, publicKey, err := w.GetMasterKeyEd25519()
if err != nil {
return nil, fmt.Errorf("failed to get master key: %w", err)
}
// Create DID
did, err := createDIDFromPublicKey(publicKey)
if err != nil {
return nil, fmt.Errorf("failed to create DID: %w", err)
}
// Create key ID
keyId := fmt.Sprintf("%s#z%s", did, base58.Encode(append(varint.ToUvarint(ED25519_CODEC_ID), publicKey...)))
// Create public JWK
publicJWK := jwk.JWK{
ALG: "EdDSA",
CRV: "Ed25519",
KID: keyId,
KTY: "OKP",
USE: "sig",
X: base64.RawURLEncoding.EncodeToString(publicKey),
}
// Create private JWK
privateJWK := jwk.JWK{
ALG: "EdDSA",
CRV: "Ed25519",
KID: keyId,
KTY: "OKP",
USE: "sig",
X: base64.RawURLEncoding.EncodeToString(publicKey),
D: base64.RawURLEncoding.EncodeToString(privateKey), // Use complete 64-byte private key
}
return &DIDKeyResult{
DID: did,
PublicJWK: publicJWK,
PrivateJWK: privateJWK,
}, nil
}
// createDIDFromPublicKey creates a DID:key from Ed25519 public key
func createDIDFromPublicKey(publicKey ed25519.PublicKey) (string, error) {
// Create multicodec identifier for Ed25519
codecBytes := varint.ToUvarint(ED25519_CODEC_ID)
// Combine codec + public key
idBytes := make([]byte, len(codecBytes)+len(publicKey))
copy(idBytes, codecBytes)
copy(idBytes[len(codecBytes):], publicKey)
// Encode with base58btc
id := base58.Encode(idBytes)
return fmt.Sprintf("did:key:z%s", id), nil
}
// GetPrivateJWK convenience function: generate private JWK from mnemonic
func GetPrivateJWK(mnemonic string) (*DIDKeyResult, error) {
wallet, err := NewHDWalletFromMnemonic(mnemonic, "")
if err != nil {
return nil, fmt.Errorf("failed to create HD wallet: %w", err)
}
return wallet.GetPrivateJWKTrustWalletCore()
}
// GetDID convenience function: generate DID from mnemonic
func GetDID(mnemonic string) (string, error) {
result, err := GetPrivateJWK(mnemonic)
if err != nil {
return "", err
}
return result.DID, nil
}
// GetPublicJWK convenience function: generate public JWK from mnemonic
func GetPublicJWK(mnemonic string) (*jwk.JWK, error) {
result, err := GetPrivateJWK(mnemonic)
if err != nil {
return nil, err
}
return &result.PublicJWK, nil
}
// GenerateMnemonic generates new BIP39 mnemonic
func GenerateMnemonic() string {
// Generate 128 bits of entropy for 12-word mnemonic
entropy, err := bip39.NewEntropy(128)
if err != nil {
panic(fmt.Sprintf("Failed to generate entropy for mnemonic: %v", err))
}
mnemonic, err := bip39.NewMnemonic(entropy)
if err != nil {
panic(fmt.Sprintf("Failed to generate mnemonic from entropy: %v", err))
}
return mnemonic
}