github.com/zntrio/harp/v2@v2.0.9/pkg/container/seal/v2/seal.go (about)

     1  // Licensed to Elasticsearch B.V. under one or more contributor
     2  // license agreements. See the NOTICE file distributed with
     3  // this work for additional information regarding copyright
     4  // ownership. Elasticsearch B.V. licenses this file to you under
     5  // the Apache License, Version 2.0 (the "License"); you may
     6  // not use this file except in compliance with the License.
     7  // You may obtain a copy of the License at
     8  //
     9  //     http://www.apache.org/licenses/LICENSE-2.0
    10  //
    11  // Unless required by applicable law or agreed to in writing,
    12  // software distributed under the License is distributed on an
    13  // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
    14  // KIND, either express or implied.  See the License for the
    15  // specific language governing permissions and limitations
    16  // under the License.
    17  
    18  package v2
    19  
    20  import (
    21  	"crypto/ecdsa"
    22  	"crypto/elliptic"
    23  	"errors"
    24  	"fmt"
    25  	"io"
    26  
    27  	"github.com/awnumar/memguard"
    28  
    29  	containerv1 "github.com/zntrio/harp/v2/api/gen/go/harp/container/v1"
    30  	"github.com/zntrio/harp/v2/pkg/sdk/types"
    31  )
    32  
    33  // Seal a secret container with identities.
    34  func (a *adapter) Seal(rand io.Reader, container *containerv1.Container, encodedPeersPublicKey ...string) (*containerv1.Container, error) {
    35  	return a.seal(rand, container, nil, encodedPeersPublicKey...)
    36  }
    37  
    38  // Seal a secret container with identities and preshared key.
    39  func (a *adapter) SealWithPSK(rand io.Reader, container *containerv1.Container, preSharedKey *memguard.LockedBuffer, encodedPeersPublicKey ...string) (*containerv1.Container, error) {
    40  	return a.seal(rand, container, preSharedKey, encodedPeersPublicKey...)
    41  }
    42  
    43  func (a *adapter) seal(rand io.Reader, container *containerv1.Container, preSharedKey *memguard.LockedBuffer, encodedPeersPublicKey ...string) (*containerv1.Container, error) {
    44  	// Check parameters
    45  	if types.IsNil(container) {
    46  		return nil, fmt.Errorf("unable to process nil container")
    47  	}
    48  	if types.IsNil(container.Headers) {
    49  		return nil, fmt.Errorf("unable to process nil container headers")
    50  	}
    51  	if len(encodedPeersPublicKey) == 0 {
    52  		return nil, fmt.Errorf("unable to process empty public keys")
    53  	}
    54  
    55  	// Convert public keys
    56  	peersPublicKey, err := a.publicKeys(encodedPeersPublicKey...)
    57  	if err != nil {
    58  		return nil, fmt.Errorf("unable to convert peer public keys: %w", err)
    59  	}
    60  
    61  	// Generate encryption key
    62  	payloadKey, err := generatedEncryptionKey(rand)
    63  	if err != nil {
    64  		return nil, fmt.Errorf("unable to generate encryption key: %w", err)
    65  	}
    66  
    67  	// Prepare signature identity
    68  	sigPriv, encryptedPubSig, err := prepareSignature(rand, payloadKey)
    69  	if err != nil {
    70  		return nil, fmt.Errorf("unable to prepare signature materials: %w", err)
    71  	}
    72  
    73  	// Generate ephemeral encryption key
    74  	encPriv, err := ecdsa.GenerateKey(encryptionCurve, rand)
    75  	if err != nil {
    76  		return nil, fmt.Errorf("unable to generate ephemeral encryption keypair")
    77  	}
    78  
    79  	// Prepare sealed container
    80  	containerHeaders := &containerv1.Header{
    81  		ContentType:         containerSealedContentType,
    82  		EncryptionPublicKey: elliptic.MarshalCompressed(encPriv.Curve, encPriv.PublicKey.X, encPriv.PublicKey.Y),
    83  		ContainerBox:        encryptedPubSig,
    84  		Recipients:          []*containerv1.Recipient{},
    85  		SealVersion:         SealVersion,
    86  	}
    87  
    88  	// Compute preshared key
    89  	var psk *[preSharedKeySize]byte
    90  	if preSharedKey != nil {
    91  		psk = pskStretch(preSharedKey.Bytes(), containerHeaders.EncryptionPublicKey)
    92  	}
    93  
    94  	// Process recipients
    95  	for _, peerPublicKey := range peersPublicKey {
    96  		// Ignore nil key
    97  		if peerPublicKey == nil {
    98  			continue
    99  		}
   100  
   101  		// Pack recipient using its public key
   102  		r, errPack := packRecipient(rand, payloadKey, encPriv, peerPublicKey, psk)
   103  		if errPack != nil {
   104  			return nil, fmt.Errorf("unable to pack container recipient (%X): %w", *peerPublicKey, err)
   105  		}
   106  
   107  		// Append to container
   108  		containerHeaders.Recipients = append(containerHeaders.Recipients, r)
   109  	}
   110  
   111  	// Sanity check
   112  	if len(containerHeaders.Recipients) == 0 {
   113  		return nil, errors.New("unable to seal a container without recipients")
   114  	}
   115  
   116  	// Sign given container
   117  	content, containerSig, err := signContainer(sigPriv, containerHeaders, container)
   118  	if err != nil {
   119  		return nil, fmt.Errorf("unable to sign container data: %w", err)
   120  	}
   121  
   122  	// Encrypt payload
   123  	encryptedPayload, err := encrypt(rand, append(containerSig, content...), payloadKey)
   124  	if err != nil {
   125  		return nil, fmt.Errorf("unable to encrypt container data: %w", err)
   126  	}
   127  
   128  	// No error
   129  	return &containerv1.Container{
   130  		Headers: containerHeaders,
   131  		Raw:     encryptedPayload,
   132  	}, nil
   133  }