448 lines
17 KiB
Go
448 lines
17 KiB
Go
// Copyright 2021 Contributors to the Parsec project.
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package parsec
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
|
|
"github.com/parallaxsecond/parsec-client-go/interface/auth"
|
|
"github.com/parallaxsecond/parsec-client-go/interface/operations"
|
|
"github.com/parallaxsecond/parsec-client-go/interface/requests"
|
|
"github.com/parallaxsecond/parsec-client-go/parsec/algorithm"
|
|
)
|
|
|
|
// BasicClient is a Parsec client representing a connection and set of API implementations
|
|
type BasicClient struct {
|
|
opclient *operations.Client
|
|
auth Authenticator
|
|
implicitProvider ProviderID
|
|
}
|
|
|
|
// CreateNakedClient creates a Parsec client, setting implicit provider to ProviderCore and
|
|
// setting the authenticator to NoAuth.
|
|
func CreateNakedClient() (*BasicClient, error) {
|
|
opclient, err := operations.InitClient()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bc := BasicClient{
|
|
opclient: opclient,
|
|
implicitProvider: ProviderCore,
|
|
auth: NewNoAuthAuthenticator(),
|
|
}
|
|
return &bc, nil
|
|
}
|
|
|
|
// CreateConfiguredClient initializes a Parsec client
|
|
// This will autoselect the first provider returned by the parsec service. It will also attempt to
|
|
// select the first available authenticator it can configure. The config can either be a *ClientConfig or a string.
|
|
// If it is a string, then this is used as an application name if the default authenticator is the Direct Authenticator - it will be ignored otherwise.
|
|
// If nil is passed, then the client will try and find the first supported authenticator that requires no configuration.
|
|
func CreateConfiguredClient(config interface{}) (*BasicClient, error) {
|
|
var clientConfig *ClientConfig
|
|
if config == nil {
|
|
clientConfig = NewClientConfig()
|
|
} else {
|
|
switch confSpecific := config.(type) {
|
|
case string:
|
|
clientConfig = DirectAuthConfigData(confSpecific)
|
|
case *ClientConfig:
|
|
clientConfig = confSpecific
|
|
default:
|
|
return nil, fmt.Errorf("could not create configuration from type %v", reflect.TypeOf(config))
|
|
}
|
|
}
|
|
|
|
var opclient *operations.Client
|
|
var err error
|
|
if clientConfig.connection == nil {
|
|
opclient, err = operations.InitClient()
|
|
} else {
|
|
opclient, err = operations.InitClientFromConnection(clientConfig.connection)
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bc := BasicClient{
|
|
opclient: opclient,
|
|
implicitProvider: ProviderCore,
|
|
auth: NewNoAuthAuthenticator(),
|
|
}
|
|
|
|
if clientConfig.defaultProvider != nil {
|
|
bc.implicitProvider = *clientConfig.defaultProvider
|
|
} else {
|
|
err = bc.selectDefaultProvider()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if clientConfig.authenticator != nil {
|
|
bc.auth = clientConfig.authenticator
|
|
} else {
|
|
err = bc.selectDefaultAuthenticator(clientConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return &bc, nil
|
|
}
|
|
|
|
// Close the client and any underlying connections
|
|
func (c *BasicClient) Close() error {
|
|
return c.opclient.Close()
|
|
}
|
|
|
|
// SetImplicitProvider sets the provider to use for non-core operations
|
|
func (c *BasicClient) SetImplicitProvider(provider ProviderID) {
|
|
c.implicitProvider = provider
|
|
}
|
|
|
|
// GetImplicitProvider returns the provider used for non-core operations
|
|
func (c *BasicClient) GetImplicitProvider() ProviderID {
|
|
return c.implicitProvider
|
|
}
|
|
|
|
// selectDefaultProvider will request the list of providers from the parsec service and then select the first one.
|
|
func (c *BasicClient) selectDefaultProvider() error {
|
|
c.implicitProvider = ProviderCore // We know this one is always present.
|
|
availableProviders, err := c.ListProviders()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if len(availableProviders) > 0 {
|
|
c.implicitProvider = availableProviders[0].ID
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// selectDefaultAuthenticator will request the list of authenticators from the parsec service and will then select
|
|
// the first one it is able to configure automatically
|
|
func (c *BasicClient) selectDefaultAuthenticator(config *ClientConfig) error {
|
|
availableAuthenticators, err := c.ListAuthenticators()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
Loop:
|
|
for _, authenticator := range availableAuthenticators {
|
|
switch authenticator.ID { //nolint:exhaustive // we cover everything with the default
|
|
case AuthDirect:
|
|
// See if we have data for this authenticator type
|
|
if data, ok := config.authenticatorData[auth.AuthDirect]; ok {
|
|
if appName, ok := data.(string); ok {
|
|
c.auth = NewDirectAuthenticator(appName)
|
|
break Loop
|
|
} else {
|
|
panic("Direct authenticator data is of wrong type.") // this should not happen
|
|
}
|
|
} // no data for this authenticator, carry on trying
|
|
case AuthUnixPeerCredentials:
|
|
c.auth = NewUnixPeerAuthenticator()
|
|
break Loop
|
|
default:
|
|
continue
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetAuthenticatorType returns the type of authenticator currently in use
|
|
func (c *BasicClient) GetAuthenticatorType() AuthenticatorType {
|
|
return c.auth.GetAuthenticatorType()
|
|
}
|
|
|
|
// Ping server and return wire protocol major and minor version number
|
|
func (c BasicClient) Ping() (uint8, uint8, error) { //nolint:gocritic
|
|
return c.opclient.Ping(requests.ProviderCore, c.auth.toNativeAuthenticator())
|
|
}
|
|
|
|
// ListProviders returns a list of the providers supported by the server.
|
|
func (c BasicClient) ListProviders() ([]*ProviderInfo, error) {
|
|
nativeProv, err := c.opclient.ListProviders(requests.ProviderCore, c.auth.toNativeAuthenticator())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
providers := make([]*ProviderInfo, len(nativeProv))
|
|
for i, p := range nativeProv {
|
|
providers[i] = newProviderInfoFromOp(p)
|
|
}
|
|
return providers, nil
|
|
}
|
|
|
|
// ListOpcodes list the opcodes for a provider
|
|
func (c BasicClient) ListOpcodes(providerID ProviderID) ([]uint32, error) {
|
|
return c.opclient.ListOpcodes(requests.ProviderCore, c.auth.toNativeAuthenticator(), uint32(providerID))
|
|
}
|
|
|
|
// ListClients lists the clients. Requires admin privileges
|
|
func (c BasicClient) ListClients() ([]string, error) {
|
|
return c.opclient.ListClients(requests.ProviderID(ProviderCore), c.auth.toNativeAuthenticator())
|
|
}
|
|
|
|
// Delete a client. Requires admin privileges
|
|
func (c BasicClient) DeleteClient(client string) error {
|
|
return c.opclient.DeleteClient(requests.ProviderID(ProviderCore), c.auth.toNativeAuthenticator(), client)
|
|
}
|
|
|
|
// ListKeys obtain keys stored for current application
|
|
func (c BasicClient) ListKeys() ([]*KeyInfo, error) {
|
|
retkeys, err := c.opclient.ListKeys(requests.ProviderCore, c.auth.toNativeAuthenticator())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
keys := make([]*KeyInfo, len(retkeys))
|
|
for idx, key := range retkeys {
|
|
keys[idx], err = newKeyInfoFromOp(key)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return keys, nil
|
|
}
|
|
|
|
// ListAuthenticators obtain authenticators supported by server
|
|
func (c BasicClient) ListAuthenticators() ([]*AuthenticatorInfo, error) {
|
|
retauths, err := c.opclient.ListAuthenticators(requests.ProviderCore, c.auth.toNativeAuthenticator())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
auths := make([]*AuthenticatorInfo, len(retauths))
|
|
for idx, auth := range retauths {
|
|
a, err := newAuthenticatorInfoFromOp(auth)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
auths[idx] = a
|
|
}
|
|
return auths, nil
|
|
}
|
|
|
|
// PsaGenerateKey create key named name with attributes
|
|
func (c BasicClient) PsaGenerateKey(name string, attributes *KeyAttributes) error {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
|
|
ka, err := attributes.toWireInterface()
|
|
|
|
if err != nil {
|
|
return err
|
|
}
|
|
fmt.Printf("keyattributes: %+v\n", ka)
|
|
return c.opclient.PsaGenerateKey(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), name, ka)
|
|
}
|
|
|
|
// PsaDestroyKey destroys a key with given name
|
|
func (c BasicClient) PsaDestroyKey(name string) error {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
return c.opclient.PsaDestroyKey(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), name)
|
|
}
|
|
|
|
// PsaHashCompute calculates a hash of a message using specified algorithm
|
|
func (c BasicClient) PsaHashCompute(message []byte, alg algorithm.HashAlgorithmType) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
return c.opclient.PsaHashCompute(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), message, hashAlgToWire(alg))
|
|
}
|
|
|
|
// PsaSignMessage signs message using signingKey and algorithm, returning the signature.
|
|
func (c BasicClient) PsaSignMessage(signingKey string, message []byte, alg *algorithm.AsymmetricSignatureAlgorithm) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algAsymmetricSigToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaSignMessage(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), signingKey, message, opalg)
|
|
}
|
|
|
|
// PsaSignHash signs hash using signingKey and algorithm, returning the signature.
|
|
func (c BasicClient) PsaSignHash(signingKey string, hash []byte, alg *algorithm.AsymmetricSignatureAlgorithm) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algAsymmetricSigToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaSignHash(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), signingKey, hash, opalg)
|
|
}
|
|
|
|
// PsaVerifyMessage verify a signature of message with verifyingKey using signature algorithm alg.
|
|
func (c BasicClient) PsaVerifyMessage(verifyingKey string, message, signature []byte, alg *algorithm.AsymmetricSignatureAlgorithm) error {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algAsymmetricSigToWire(alg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.opclient.PsaVerifyMessage(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), verifyingKey, message, signature, opalg)
|
|
}
|
|
|
|
// PsaVerifyHash verify a signature of hash with verifyingKey using signature algorithm alg.
|
|
func (c BasicClient) PsaVerifyHash(verifyingKey string, hash, signature []byte, alg *algorithm.AsymmetricSignatureAlgorithm) error {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algAsymmetricSigToWire(alg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.opclient.PsaVerifyHash(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), verifyingKey, hash, signature, opalg)
|
|
}
|
|
|
|
// PsaCipherEncrypt carries out symmetric encryption on plaintext using defined key/algorithm, returning ciphertext
|
|
func (c BasicClient) PsaCipherEncrypt(keyName string, alg *algorithm.Cipher, plaintext []byte) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algCipherAlgToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaCipherEncrypt(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName, opalg, plaintext)
|
|
}
|
|
|
|
// PsaCipherDecrypt decrypts symmetrically encrypted ciphertext using defined key/algorithm, returning plaintext
|
|
func (c BasicClient) PsaCipherDecrypt(keyName string, alg *algorithm.Cipher, ciphertext []byte) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algCipherAlgToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaCipherDecrypt(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName, opalg, ciphertext)
|
|
}
|
|
|
|
// PsaAeadDecrypt decrypts Aead encrypted cipher text and validates authenticates over nonce, additionalData and plaintext. Returns plaintext
|
|
func (c BasicClient) PsaAeadDecrypt(keyName string, alg *algorithm.AeadAlgorithm, nonce, additionalData, ciphertext []byte) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algAeadAlgToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaAeadDecrypt(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName, opalg, nonce, additionalData, ciphertext)
|
|
}
|
|
|
|
// PsaAeadEncrypt encrypts plaintext and provides authentication protection to plaintext, nonce and additionalData, returns ciphertext
|
|
func (c BasicClient) PsaAeadEncrypt(keyName string, alg *algorithm.AeadAlgorithm, nonce, additionalData, plaintext []byte) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algAeadAlgToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaAeadEncrypt(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName, opalg, nonce, additionalData, plaintext)
|
|
}
|
|
|
|
// PsaExportKey exports the key, if it is exportable.
|
|
func (c BasicClient) PsaExportKey(keyName string) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
return c.opclient.PsaExportKey(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName)
|
|
}
|
|
|
|
// PsaImportKey imports a key and gives it the specified attributes
|
|
func (c BasicClient) PsaImportKey(keyName string, attributes *KeyAttributes, data []byte) error {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opattrs, err := attributes.toWireInterface()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.opclient.PsaImportKey(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName, opattrs, data)
|
|
}
|
|
|
|
// PsaExportPublicKey exports a public key.
|
|
func (c BasicClient) PsaExportPublicKey(keyName string) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
return c.opclient.PsaExportPublicKey(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName)
|
|
}
|
|
|
|
// PsaGenerateRandom generates size bytes of random data
|
|
func (c BasicClient) PsaGenerateRandom(size uint64) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
return c.opclient.PsaGenerateRandom(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), size)
|
|
}
|
|
|
|
// PsaMACCompute computes a mac over the input, using defined key, using the defined algorithm. Returns the mac.
|
|
func (c BasicClient) PsaMACCompute(keyName string, alg *algorithm.MacAlgorithm, input []byte) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algMacAlgToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaMACCompute(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName, opalg, input)
|
|
}
|
|
|
|
// PsaMACVerify verifies the supplied mac matches the input, for the defined key and algorithm.
|
|
func (c BasicClient) PsaMACVerify(keyName string, alg *algorithm.MacAlgorithm, input, mac []byte) error {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algMacAlgToWire(alg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return c.opclient.PsaMACVerify(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName, opalg, input, mac)
|
|
}
|
|
|
|
// PsaRawKeyAgreement creates a key agreement using specified algorithm and keys.
|
|
func (c BasicClient) PsaRawKeyAgreement(alg *algorithm.KeyAgreementRaw, privateKey string, peerKey []byte) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algKeyAgreementRawAlgToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaRawKeyAgreement(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), opalg.GetRaw().Enum(), privateKey, peerKey)
|
|
}
|
|
|
|
// PsaAsymmetricDecrypt decrypt ciphertext using specified key and asymmetric algorithm. Returns plaintext.
|
|
func (c BasicClient) PsaAsymmetricDecrypt(keyName string, alg *algorithm.AsymmetricEncryptionAlgorithm, salt, ciphertext []byte) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algAsymmetricEncryptionAlgToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaAsymmetricDecrypt(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName, opalg, salt, ciphertext)
|
|
}
|
|
|
|
// PsaAsymmetricEncrypt encrypt plaintext using specified asymmetric key and algorithm. Returns ciphertext.
|
|
func (c BasicClient) PsaAsymmetricEncrypt(keyName string, alg *algorithm.AsymmetricEncryptionAlgorithm, salt, plaintext []byte) ([]byte, error) {
|
|
if !c.implicitProvider.HasCrypto() {
|
|
return nil, fmt.Errorf("provider does not support crypto operation")
|
|
}
|
|
opalg, err := algAsymmetricEncryptionAlgToWire(alg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return c.opclient.PsaAsymmetricEncrypt(requests.ProviderID(c.implicitProvider), c.auth.toNativeAuthenticator(), keyName, opalg, salt, plaintext)
|
|
}
|