Files
crypto/mpc/import.go

141 lines
4.0 KiB
Go
Raw Permalink Normal View History

2025-10-09 15:10:39 -04:00
package mpc
import (
"encoding/hex"
"errors"
"fmt"
)
// ImportEnclave creates an Enclave instance from various import options.
// It prioritizes enclave bytes over keyshares if both are provided.
func ImportEnclave(options ...ImportOption) (Enclave, error) {
if len(options) == 0 {
return nil, errors.New("no import options provided")
}
opts := Options{}
for _, opt := range options {
opts = opt(opts)
}
return opts.Apply()
}
// Options is a struct that holds the import options
type Options struct {
valKeyshare Message
userKeyshare Message
enclaveBytes []byte
enclaveData *EnclaveData
initialShares bool
isEncrypted bool
secretKey []byte
curve CurveName
}
// ImportOption is a function that modifies the import options
type ImportOption func(Options) Options
// WithInitialShares creates an option to import an enclave from validator and user keyshares.
func WithInitialShares(valKeyshare Message, userKeyshare Message, curve CurveName) ImportOption {
return func(opts Options) Options {
opts.valKeyshare = valKeyshare
opts.userKeyshare = userKeyshare
opts.initialShares = true
opts.curve = curve
return opts
}
}
// WithEncryptedData creates an option to import an enclave from encrypted data.
func WithEncryptedData(data []byte, key []byte) ImportOption {
return func(opts Options) Options {
opts.enclaveBytes = data
opts.initialShares = false
opts.isEncrypted = true
opts.secretKey = key
return opts
}
}
// WithEnclaveData creates an option to import an enclave from a data struct.
func WithEnclaveData(data *EnclaveData) ImportOption {
return func(opts Options) Options {
opts.enclaveData = data
opts.initialShares = false
return opts
}
}
// Apply applies the import options to create an Enclave instance.
func (opts Options) Apply() (Enclave, error) {
// Load from encrypted data if provided
if opts.isEncrypted {
if len(opts.enclaveBytes) == 0 {
return nil, errors.New("enclave bytes cannot be empty")
}
return RestoreEncryptedEnclave(opts.enclaveBytes, opts.secretKey)
}
// Generate from keyshares if provided
if opts.initialShares {
// Then try to build from keyshares
if opts.valKeyshare == nil {
return nil, errors.New("validator share cannot be nil")
}
if opts.userKeyshare == nil {
return nil, errors.New("user share cannot be nil")
}
return BuildEnclave(opts.valKeyshare, opts.userKeyshare, opts)
}
// Load from enclave data if provided
return RestoreEnclaveFromData(opts.enclaveData)
}
// BuildEnclave creates a new enclave from validator and user keyshares.
func BuildEnclave(valShare, userShare Message, options Options) (Enclave, error) {
if valShare == nil {
return nil, errors.New("validator share cannot be nil")
}
if userShare == nil {
return nil, errors.New("user share cannot be nil")
}
pubPoint, err := GetAlicePublicPoint(valShare)
if err != nil {
return nil, fmt.Errorf("failed to get public point: %w", err)
}
return &EnclaveData{
PubBytes: pubPoint.ToAffineUncompressed(),
PubHex: hex.EncodeToString(pubPoint.ToAffineCompressed()),
ValShare: valShare,
UserShare: userShare,
Nonce: randNonce(),
Curve: options.curve,
}, nil
}
// RestoreEnclaveFromData deserializes an enclave from its data struct.
func RestoreEnclaveFromData(data *EnclaveData) (Enclave, error) {
if data == nil {
return nil, errors.New("enclave data cannot be nil")
}
return data, nil
}
// RestoreEncryptedEnclave decrypts an enclave from its binary representation. and key
func RestoreEncryptedEnclave(data []byte, key []byte) (Enclave, error) {
keyclave := &EnclaveData{}
err := keyclave.Unmarshal(data)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal enclave: %w", err)
}
decryptedData, err := keyclave.Decrypt(key, data)
if err != nil {
return nil, fmt.Errorf("failed to decrypt enclave: %w", err)
}
err = keyclave.Unmarshal(decryptedData)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal decrypted enclave: %w", err)
}
return keyclave, nil
}