Implements quantum-resistant cryptographic primitives across all SDK languages: - Hybrid Ed25519 + Dilithium3 signatures (classical + post-quantum) - BIP-39 mnemonic support (12, 15, 18, 21, 24 words) - BIP-44 hierarchical key derivation (coin type 0x5359) - Post-quantum algorithms: Falcon (FIPS 206), SPHINCS+ (FIPS 205) - Key derivation: HKDF-SHA3-256, PBKDF2 - Hash functions: SHA3-256, BLAKE3, Keccak-256 Languages: JavaScript/TypeScript, Python, Go, Rust, Flutter/Dart, Java, Kotlin, Swift, C, C++, C#/.NET, Ruby
691 lines
20 KiB
Go
691 lines
20 KiB
Go
// Package crypto provides quantum-resistant cryptographic primitives for the Synor blockchain.
|
|
package crypto
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"time"
|
|
)
|
|
|
|
// SynorCrypto is the main client for the Crypto SDK
|
|
type SynorCrypto struct {
|
|
config Config
|
|
client *http.Client
|
|
closed bool
|
|
|
|
Mnemonic *MnemonicClient
|
|
Keypairs *KeypairClient
|
|
Signing *SigningClient
|
|
Falcon *FalconClient
|
|
Sphincs *SphincsClient
|
|
KDF *KDFClient
|
|
Hash *HashClient
|
|
Negotiation *NegotiationClient
|
|
}
|
|
|
|
// New creates a new SynorCrypto client
|
|
func New(config Config) *SynorCrypto {
|
|
if config.Endpoint == "" {
|
|
config.Endpoint = DefaultEndpoint
|
|
}
|
|
if config.Timeout == 0 {
|
|
config.Timeout = 30000
|
|
}
|
|
if config.Retries == 0 {
|
|
config.Retries = 3
|
|
}
|
|
if config.DefaultNetwork == "" {
|
|
config.DefaultNetwork = NetworkMainnet
|
|
}
|
|
|
|
c := &SynorCrypto{
|
|
config: config,
|
|
client: &http.Client{
|
|
Timeout: time.Duration(config.Timeout) * time.Millisecond,
|
|
},
|
|
}
|
|
|
|
c.Mnemonic = &MnemonicClient{crypto: c}
|
|
c.Keypairs = &KeypairClient{crypto: c}
|
|
c.Signing = &SigningClient{crypto: c}
|
|
c.Falcon = &FalconClient{crypto: c}
|
|
c.Sphincs = &SphincsClient{crypto: c}
|
|
c.KDF = &KDFClient{crypto: c}
|
|
c.Hash = &HashClient{crypto: c}
|
|
c.Negotiation = &NegotiationClient{crypto: c}
|
|
|
|
return c
|
|
}
|
|
|
|
// DefaultNetwork returns the default network
|
|
func (c *SynorCrypto) DefaultNetwork() Network {
|
|
return c.config.DefaultNetwork
|
|
}
|
|
|
|
// HealthCheck checks if the service is healthy
|
|
func (c *SynorCrypto) HealthCheck(ctx context.Context) (bool, error) {
|
|
var result map[string]interface{}
|
|
if err := c.get(ctx, "/health", &result); err != nil {
|
|
return false, nil
|
|
}
|
|
status, _ := result["status"].(string)
|
|
return status == "healthy", nil
|
|
}
|
|
|
|
// GetInfo returns service information
|
|
func (c *SynorCrypto) GetInfo(ctx context.Context) (map[string]interface{}, error) {
|
|
var result map[string]interface{}
|
|
err := c.get(ctx, "/info", &result)
|
|
return result, err
|
|
}
|
|
|
|
// Close closes the client
|
|
func (c *SynorCrypto) Close() {
|
|
c.closed = true
|
|
}
|
|
|
|
// IsClosed returns true if the client is closed
|
|
func (c *SynorCrypto) IsClosed() bool {
|
|
return c.closed
|
|
}
|
|
|
|
func (c *SynorCrypto) get(ctx context.Context, path string, result interface{}) error {
|
|
if c.closed {
|
|
return NewCryptoError("Client has been closed", "CLIENT_CLOSED")
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "GET", c.config.Endpoint+path, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.setHeaders(req)
|
|
|
|
resp, err := c.client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
return c.handleResponse(resp, result)
|
|
}
|
|
|
|
func (c *SynorCrypto) post(ctx context.Context, path string, body interface{}, result interface{}) error {
|
|
if c.closed {
|
|
return NewCryptoError("Client has been closed", "CLIENT_CLOSED")
|
|
}
|
|
|
|
var reqBody io.Reader
|
|
if body != nil {
|
|
jsonBody, err := json.Marshal(body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
reqBody = bytes.NewBuffer(jsonBody)
|
|
}
|
|
|
|
req, err := http.NewRequestWithContext(ctx, "POST", c.config.Endpoint+path, reqBody)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
c.setHeaders(req)
|
|
|
|
resp, err := c.client.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
return c.handleResponse(resp, result)
|
|
}
|
|
|
|
func (c *SynorCrypto) setHeaders(req *http.Request) {
|
|
req.Header.Set("Authorization", "Bearer "+c.config.APIKey)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
req.Header.Set("X-SDK-Version", "go/0.1.0")
|
|
}
|
|
|
|
func (c *SynorCrypto) handleResponse(resp *http.Response, result interface{}) error {
|
|
body, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if resp.StatusCode >= 400 {
|
|
var errResp map[string]interface{}
|
|
json.Unmarshal(body, &errResp)
|
|
msg := fmt.Sprintf("HTTP %d", resp.StatusCode)
|
|
code := ""
|
|
if m, ok := errResp["message"].(string); ok {
|
|
msg = m
|
|
}
|
|
if c, ok := errResp["code"].(string); ok {
|
|
code = c
|
|
}
|
|
return NewCryptoError(msg, code)
|
|
}
|
|
|
|
return json.Unmarshal(body, result)
|
|
}
|
|
|
|
// ============================================================================
|
|
// Mnemonic Client
|
|
// ============================================================================
|
|
|
|
// MnemonicClient handles mnemonic operations
|
|
type MnemonicClient struct {
|
|
crypto *SynorCrypto
|
|
}
|
|
|
|
// Generate creates a new random mnemonic
|
|
func (c *MnemonicClient) Generate(ctx context.Context, wordCount int) (*Mnemonic, error) {
|
|
var result mnemonicResponse
|
|
err := c.crypto.post(ctx, "/mnemonic/generate", map[string]int{"word_count": wordCount}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result.toMnemonic()
|
|
}
|
|
|
|
// FromPhrase creates a mnemonic from a phrase
|
|
func (c *MnemonicClient) FromPhrase(ctx context.Context, phrase string) (*Mnemonic, error) {
|
|
var result mnemonicResponse
|
|
err := c.crypto.post(ctx, "/mnemonic/from-phrase", map[string]string{"phrase": phrase}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result.toMnemonic()
|
|
}
|
|
|
|
// FromEntropy creates a mnemonic from entropy
|
|
func (c *MnemonicClient) FromEntropy(ctx context.Context, entropy []byte) (*Mnemonic, error) {
|
|
var result mnemonicResponse
|
|
err := c.crypto.post(ctx, "/mnemonic/from-entropy", map[string]string{
|
|
"entropy": EncodeBase64(entropy),
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result.toMnemonic()
|
|
}
|
|
|
|
// Validate checks if a phrase is valid
|
|
func (c *MnemonicClient) Validate(ctx context.Context, phrase string) (*MnemonicValidation, error) {
|
|
var result MnemonicValidation
|
|
err := c.crypto.post(ctx, "/mnemonic/validate", map[string]string{"phrase": phrase}, &result)
|
|
return &result, err
|
|
}
|
|
|
|
// ToSeed derives a seed from a mnemonic
|
|
func (c *MnemonicClient) ToSeed(ctx context.Context, phrase string, passphrase string) ([]byte, error) {
|
|
var result struct {
|
|
Seed string `json:"seed"`
|
|
}
|
|
err := c.crypto.post(ctx, "/mnemonic/to-seed", map[string]string{
|
|
"phrase": phrase,
|
|
"passphrase": passphrase,
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return DecodeBase64(result.Seed)
|
|
}
|
|
|
|
// SuggestWords suggests completions for a partial word
|
|
func (c *MnemonicClient) SuggestWords(ctx context.Context, partial string, limit int) ([]string, error) {
|
|
var result struct {
|
|
Suggestions []string `json:"suggestions"`
|
|
}
|
|
err := c.crypto.post(ctx, "/mnemonic/suggest", map[string]interface{}{
|
|
"partial": partial,
|
|
"limit": limit,
|
|
}, &result)
|
|
return result.Suggestions, err
|
|
}
|
|
|
|
type mnemonicResponse struct {
|
|
Phrase string `json:"phrase"`
|
|
Words []string `json:"words"`
|
|
WordCount int `json:"word_count"`
|
|
Entropy string `json:"entropy"`
|
|
}
|
|
|
|
func (r *mnemonicResponse) toMnemonic() (*Mnemonic, error) {
|
|
entropy, _ := DecodeBase64(r.Entropy)
|
|
return &Mnemonic{
|
|
Phrase: r.Phrase,
|
|
Words: r.Words,
|
|
WordCount: r.WordCount,
|
|
Entropy: entropy,
|
|
}, nil
|
|
}
|
|
|
|
// ============================================================================
|
|
// Keypair Client
|
|
// ============================================================================
|
|
|
|
// KeypairClient handles keypair operations
|
|
type KeypairClient struct {
|
|
crypto *SynorCrypto
|
|
}
|
|
|
|
// Generate creates a new random keypair
|
|
func (c *KeypairClient) Generate(ctx context.Context) (*HybridKeypair, error) {
|
|
var result keypairResponse
|
|
err := c.crypto.post(ctx, "/keypair/generate", nil, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result.toKeypair()
|
|
}
|
|
|
|
// FromMnemonic creates a keypair from a mnemonic
|
|
func (c *KeypairClient) FromMnemonic(ctx context.Context, phrase string, passphrase string) (*HybridKeypair, error) {
|
|
var result keypairResponse
|
|
err := c.crypto.post(ctx, "/keypair/from-mnemonic", map[string]string{
|
|
"phrase": phrase,
|
|
"passphrase": passphrase,
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result.toKeypair()
|
|
}
|
|
|
|
// FromSeed creates a keypair from a seed
|
|
func (c *KeypairClient) FromSeed(ctx context.Context, seed []byte) (*HybridKeypair, error) {
|
|
var result keypairResponse
|
|
err := c.crypto.post(ctx, "/keypair/from-seed", map[string]string{
|
|
"seed": EncodeBase64(seed),
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return result.toKeypair()
|
|
}
|
|
|
|
// GetAddress gets the address for a public key
|
|
func (c *KeypairClient) GetAddress(ctx context.Context, pk *HybridPublicKey, network Network) (*Address, error) {
|
|
var result Address
|
|
err := c.crypto.post(ctx, "/keypair/address", map[string]interface{}{
|
|
"public_key": map[string]string{
|
|
"ed25519": EncodeBase64(pk.Ed25519),
|
|
"dilithium": EncodeBase64(pk.Dilithium),
|
|
},
|
|
"network": network,
|
|
}, &result)
|
|
return &result, err
|
|
}
|
|
|
|
type keypairResponse struct {
|
|
PublicKey struct {
|
|
Ed25519 string `json:"ed25519"`
|
|
Dilithium string `json:"dilithium"`
|
|
} `json:"public_key"`
|
|
SecretKey struct {
|
|
Ed25519Seed string `json:"ed25519_seed"`
|
|
MasterSeed string `json:"master_seed"`
|
|
} `json:"secret_key"`
|
|
Addresses map[string]string `json:"addresses"`
|
|
}
|
|
|
|
func (r *keypairResponse) toKeypair() (*HybridKeypair, error) {
|
|
ed25519, _ := DecodeBase64(r.PublicKey.Ed25519)
|
|
dilithium, _ := DecodeBase64(r.PublicKey.Dilithium)
|
|
ed25519Seed, _ := DecodeBase64(r.SecretKey.Ed25519Seed)
|
|
masterSeed, _ := DecodeBase64(r.SecretKey.MasterSeed)
|
|
|
|
addresses := make(map[Network]string)
|
|
for k, v := range r.Addresses {
|
|
addresses[Network(k)] = v
|
|
}
|
|
|
|
return &HybridKeypair{
|
|
PublicKey: &HybridPublicKey{
|
|
Ed25519: ed25519,
|
|
Dilithium: dilithium,
|
|
},
|
|
SecretKey: &SecretKey{
|
|
Ed25519Seed: ed25519Seed,
|
|
MasterSeed: masterSeed,
|
|
},
|
|
addresses: addresses,
|
|
}, nil
|
|
}
|
|
|
|
// ============================================================================
|
|
// Signing Client
|
|
// ============================================================================
|
|
|
|
// SigningClient handles signing operations
|
|
type SigningClient struct {
|
|
crypto *SynorCrypto
|
|
}
|
|
|
|
// Sign signs a message with a hybrid keypair
|
|
func (c *SigningClient) Sign(ctx context.Context, kp *HybridKeypair, message []byte) (*HybridSignature, error) {
|
|
var result struct {
|
|
Ed25519 string `json:"ed25519"`
|
|
Dilithium string `json:"dilithium"`
|
|
}
|
|
err := c.crypto.post(ctx, "/sign/hybrid", map[string]interface{}{
|
|
"secret_key": map[string]string{
|
|
"ed25519_seed": EncodeBase64(kp.SecretKey.Ed25519Seed),
|
|
"master_seed": EncodeBase64(kp.SecretKey.MasterSeed),
|
|
},
|
|
"message": EncodeBase64(message),
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ed25519, _ := DecodeBase64(result.Ed25519)
|
|
dilithium, _ := DecodeBase64(result.Dilithium)
|
|
return &HybridSignature{
|
|
Ed25519: ed25519,
|
|
Dilithium: dilithium,
|
|
}, nil
|
|
}
|
|
|
|
// Verify verifies a hybrid signature
|
|
func (c *SigningClient) Verify(ctx context.Context, pk *HybridPublicKey, message []byte, sig *HybridSignature) (bool, error) {
|
|
var result struct {
|
|
Valid bool `json:"valid"`
|
|
}
|
|
err := c.crypto.post(ctx, "/sign/verify", map[string]interface{}{
|
|
"public_key": map[string]string{
|
|
"ed25519": EncodeBase64(pk.Ed25519),
|
|
"dilithium": EncodeBase64(pk.Dilithium),
|
|
},
|
|
"message": EncodeBase64(message),
|
|
"signature": map[string]string{
|
|
"ed25519": EncodeBase64(sig.Ed25519),
|
|
"dilithium": EncodeBase64(sig.Dilithium),
|
|
},
|
|
}, &result)
|
|
return result.Valid, err
|
|
}
|
|
|
|
// SignEd25519 signs with Ed25519 only
|
|
func (c *SigningClient) SignEd25519(ctx context.Context, secretKey []byte, message []byte) ([]byte, error) {
|
|
var result struct {
|
|
Signature string `json:"signature"`
|
|
}
|
|
err := c.crypto.post(ctx, "/sign/ed25519", map[string]string{
|
|
"secret_key": EncodeBase64(secretKey),
|
|
"message": EncodeBase64(message),
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return DecodeBase64(result.Signature)
|
|
}
|
|
|
|
// ============================================================================
|
|
// Falcon Client
|
|
// ============================================================================
|
|
|
|
// FalconClient handles Falcon operations
|
|
type FalconClient struct {
|
|
crypto *SynorCrypto
|
|
}
|
|
|
|
// Generate creates a Falcon keypair
|
|
func (c *FalconClient) Generate(ctx context.Context, variant FalconVariant) (*FalconKeypair, error) {
|
|
var result map[string]interface{}
|
|
err := c.crypto.post(ctx, "/falcon/generate", map[string]string{"variant": string(variant)}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// Parse response into FalconKeypair
|
|
return parseFalconKeypair(result, variant)
|
|
}
|
|
|
|
// Sign signs with Falcon
|
|
func (c *FalconClient) Sign(ctx context.Context, kp *FalconKeypair, message []byte) (*FalconSignature, error) {
|
|
var result struct {
|
|
Signature string `json:"signature"`
|
|
Variant string `json:"variant"`
|
|
}
|
|
err := c.crypto.post(ctx, "/falcon/sign", map[string]string{
|
|
"variant": string(kp.Variant),
|
|
"secret_key": EncodeBase64(kp.SecretKey.Bytes),
|
|
"message": EncodeBase64(message),
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sigBytes, _ := DecodeBase64(result.Signature)
|
|
return &FalconSignature{
|
|
Variant: FalconVariant(result.Variant),
|
|
Bytes: sigBytes,
|
|
}, nil
|
|
}
|
|
|
|
// Verify verifies a Falcon signature
|
|
func (c *FalconClient) Verify(ctx context.Context, publicKey []byte, message []byte, sig *FalconSignature) (bool, error) {
|
|
var result struct {
|
|
Valid bool `json:"valid"`
|
|
}
|
|
err := c.crypto.post(ctx, "/falcon/verify", map[string]interface{}{
|
|
"variant": sig.Variant,
|
|
"public_key": EncodeBase64(publicKey),
|
|
"message": EncodeBase64(message),
|
|
"signature": EncodeBase64(sig.Bytes),
|
|
}, &result)
|
|
return result.Valid, err
|
|
}
|
|
|
|
func parseFalconKeypair(data map[string]interface{}, variant FalconVariant) (*FalconKeypair, error) {
|
|
pk := data["public_key"].(map[string]interface{})
|
|
sk := data["secret_key"].(map[string]interface{})
|
|
pkBytes, _ := DecodeBase64(pk["bytes"].(string))
|
|
skBytes, _ := DecodeBase64(sk["bytes"].(string))
|
|
return &FalconKeypair{
|
|
Variant: variant,
|
|
PublicKey: &FalconPublicKey{Variant: variant, Bytes: pkBytes},
|
|
SecretKey: &FalconSecretKey{Variant: variant, Bytes: skBytes},
|
|
}, nil
|
|
}
|
|
|
|
// ============================================================================
|
|
// SPHINCS+ Client
|
|
// ============================================================================
|
|
|
|
// SphincsClient handles SPHINCS+ operations
|
|
type SphincsClient struct {
|
|
crypto *SynorCrypto
|
|
}
|
|
|
|
// Generate creates a SPHINCS+ keypair
|
|
func (c *SphincsClient) Generate(ctx context.Context, variant SphincsVariant) (*SphincsKeypair, error) {
|
|
var result map[string]interface{}
|
|
err := c.crypto.post(ctx, "/sphincs/generate", map[string]string{"variant": string(variant)}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return parseSphincsKeypair(result, variant)
|
|
}
|
|
|
|
// Sign signs with SPHINCS+
|
|
func (c *SphincsClient) Sign(ctx context.Context, kp *SphincsKeypair, message []byte) (*SphincsSignature, error) {
|
|
var result struct {
|
|
Signature string `json:"signature"`
|
|
Variant string `json:"variant"`
|
|
}
|
|
err := c.crypto.post(ctx, "/sphincs/sign", map[string]string{
|
|
"variant": string(kp.Variant),
|
|
"secret_key": EncodeBase64(kp.SecretKey.Bytes),
|
|
"message": EncodeBase64(message),
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
sigBytes, _ := DecodeBase64(result.Signature)
|
|
return &SphincsSignature{
|
|
Variant: SphincsVariant(result.Variant),
|
|
Bytes: sigBytes,
|
|
}, nil
|
|
}
|
|
|
|
// Verify verifies a SPHINCS+ signature
|
|
func (c *SphincsClient) Verify(ctx context.Context, publicKey []byte, message []byte, sig *SphincsSignature) (bool, error) {
|
|
var result struct {
|
|
Valid bool `json:"valid"`
|
|
}
|
|
err := c.crypto.post(ctx, "/sphincs/verify", map[string]interface{}{
|
|
"variant": sig.Variant,
|
|
"public_key": EncodeBase64(publicKey),
|
|
"message": EncodeBase64(message),
|
|
"signature": EncodeBase64(sig.Bytes),
|
|
}, &result)
|
|
return result.Valid, err
|
|
}
|
|
|
|
func parseSphincsKeypair(data map[string]interface{}, variant SphincsVariant) (*SphincsKeypair, error) {
|
|
pk := data["public_key"].(map[string]interface{})
|
|
sk := data["secret_key"].(map[string]interface{})
|
|
pkBytes, _ := DecodeBase64(pk["bytes"].(string))
|
|
skBytes, _ := DecodeBase64(sk["bytes"].(string))
|
|
return &SphincsKeypair{
|
|
Variant: variant,
|
|
PublicKey: &SphincsPublicKey{Variant: variant, Bytes: pkBytes},
|
|
SecretKey: &SphincsSecretKey{Variant: variant, Bytes: skBytes},
|
|
}, nil
|
|
}
|
|
|
|
// ============================================================================
|
|
// KDF Client
|
|
// ============================================================================
|
|
|
|
// KDFClient handles key derivation
|
|
type KDFClient struct {
|
|
crypto *SynorCrypto
|
|
}
|
|
|
|
// DeriveKey derives a key using HKDF
|
|
func (c *KDFClient) DeriveKey(ctx context.Context, seed []byte, config *DerivationConfig) ([]byte, error) {
|
|
body := map[string]interface{}{
|
|
"seed": EncodeBase64(seed),
|
|
"output_length": config.OutputLength,
|
|
}
|
|
if config.Salt != nil {
|
|
body["salt"] = EncodeBase64(config.Salt)
|
|
}
|
|
if config.Info != nil {
|
|
body["info"] = EncodeBase64(config.Info)
|
|
}
|
|
|
|
var result struct {
|
|
Key string `json:"key"`
|
|
}
|
|
err := c.crypto.post(ctx, "/kdf/hkdf", body, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return DecodeBase64(result.Key)
|
|
}
|
|
|
|
// DeriveFromPassword derives a key from a password
|
|
func (c *KDFClient) DeriveFromPassword(ctx context.Context, password []byte, config *PasswordDerivationConfig) ([]byte, error) {
|
|
var result struct {
|
|
Key string `json:"key"`
|
|
}
|
|
err := c.crypto.post(ctx, "/kdf/pbkdf2", map[string]interface{}{
|
|
"password": EncodeBase64(password),
|
|
"salt": EncodeBase64(config.Salt),
|
|
"iterations": config.Iterations,
|
|
"output_length": config.OutputLength,
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return DecodeBase64(result.Key)
|
|
}
|
|
|
|
// DeriveChildKey derives a child key
|
|
func (c *KDFClient) DeriveChildKey(ctx context.Context, parentKey, chainCode []byte, index int) ([]byte, []byte, error) {
|
|
var result struct {
|
|
Key string `json:"key"`
|
|
ChainCode string `json:"chain_code"`
|
|
}
|
|
err := c.crypto.post(ctx, "/kdf/child", map[string]interface{}{
|
|
"parent_key": EncodeBase64(parentKey),
|
|
"chain_code": EncodeBase64(chainCode),
|
|
"index": index,
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
key, _ := DecodeBase64(result.Key)
|
|
cc, _ := DecodeBase64(result.ChainCode)
|
|
return key, cc, nil
|
|
}
|
|
|
|
// ============================================================================
|
|
// Hash Client
|
|
// ============================================================================
|
|
|
|
// HashClient handles hashing operations
|
|
type HashClient struct {
|
|
crypto *SynorCrypto
|
|
}
|
|
|
|
// SHA3_256 computes SHA3-256 hash
|
|
func (c *HashClient) SHA3_256(ctx context.Context, data []byte) (*Hash256, error) {
|
|
var result struct {
|
|
Hash string `json:"hash"`
|
|
}
|
|
err := c.crypto.post(ctx, "/hash/sha3-256", map[string]string{
|
|
"data": EncodeBase64(data),
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hashBytes, _ := DecodeBase64(result.Hash)
|
|
return &Hash256{Bytes: hashBytes, Hex: result.Hash}, nil
|
|
}
|
|
|
|
// Blake3 computes BLAKE3 hash
|
|
func (c *HashClient) Blake3(ctx context.Context, data []byte) (*Hash256, error) {
|
|
var result struct {
|
|
Hash string `json:"hash"`
|
|
}
|
|
err := c.crypto.post(ctx, "/hash/blake3", map[string]string{
|
|
"data": EncodeBase64(data),
|
|
}, &result)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
hashBytes, _ := DecodeBase64(result.Hash)
|
|
return &Hash256{Bytes: hashBytes, Hex: result.Hash}, nil
|
|
}
|
|
|
|
// ============================================================================
|
|
// Negotiation Client
|
|
// ============================================================================
|
|
|
|
// NegotiationClient handles algorithm negotiation
|
|
type NegotiationClient struct {
|
|
crypto *SynorCrypto
|
|
}
|
|
|
|
// GetCapabilities returns local capabilities
|
|
func (c *NegotiationClient) GetCapabilities(ctx context.Context) (*AlgorithmCapabilities, error) {
|
|
var result AlgorithmCapabilities
|
|
err := c.crypto.get(ctx, "/negotiation/capabilities", &result)
|
|
return &result, err
|
|
}
|
|
|
|
// Negotiate negotiates with a peer
|
|
func (c *NegotiationClient) Negotiate(ctx context.Context, peerCaps *AlgorithmCapabilities, policy *NegotiationPolicy) (*NegotiationResult, error) {
|
|
var result NegotiationResult
|
|
err := c.crypto.post(ctx, "/negotiation/negotiate", map[string]interface{}{
|
|
"peer_capabilities": peerCaps,
|
|
"policy": policy,
|
|
}, &result)
|
|
return &result, err
|
|
}
|