pkg/container/seal/v1/helpers.go (98 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package v1 import ( "bytes" "errors" "fmt" "io" "github.com/awnumar/memguard" "google.golang.org/protobuf/proto" containerv1 "github.com/elastic/harp/api/gen/go/harp/container/v1" "github.com/elastic/harp/pkg/sdk/security" "golang.org/x/crypto/blake2b" "golang.org/x/crypto/nacl/box" "golang.org/x/crypto/nacl/secretbox" ) func deriveSharedKeyFromRecipient(publicKey, privateKey *[privateKeySize]byte) *[encryptionKeySize]byte { // Prepare nonce var nonce [nonceSize]byte copy(nonce[:], "harp_derived_id_sboxkey0") // Prepare payload zero := make([]byte, 32) memguard.WipeBytes(zero) // Use box as a key agreement function var sharedKey [encryptionKeySize]byte derivedKey := box.Seal(nil, zero, &nonce, publicKey, privateKey) copy(sharedKey[:], derivedKey[len(derivedKey)-encryptionKeySize:]) // No error return &sharedKey } func computeHeaderHash(headers *containerv1.Header) ([]byte, error) { // Check arguments if headers == nil { return nil, errors.New("unable process with nil headers") } // Prepare signature header, err := proto.Marshal(headers) if err != nil { return nil, fmt.Errorf("unable to marshal container headers") } // Hash serialized proto hash := blake2b.Sum512(header) // No error return hash[:], nil } func computeProtectedHash(headerHash, content []byte) []byte { // Prepare protected content protected := bytes.Buffer{} protected.Write([]byte(signatureDomainSeparation)) protected.WriteByte(0x00) protected.Write(headerHash) contentHash := blake2b.Sum512(content) protected.Write(contentHash[:]) // No error return protected.Bytes() } func packRecipient(rand io.Reader, payloadKey, ephPrivKey, peerPublicKey *[publicKeySize]byte) (*containerv1.Recipient, error) { // Check arguments if payloadKey == nil { return nil, fmt.Errorf("unable to proceed with nil payload key") } if ephPrivKey == nil { return nil, fmt.Errorf("unable to proceed with nil private key") } if peerPublicKey == nil { return nil, fmt.Errorf("unable to proceed with nil public key") } // Create identifier recipientKey := deriveSharedKeyFromRecipient(peerPublicKey, ephPrivKey) // Calculate identifier identifier, err := keyIdentifierFromDerivedKey(recipientKey) if err != nil { return nil, fmt.Errorf("unable to derive key identifier: %w", err) } // Generate recipient nonce var recipientNonce [nonceSize]byte if _, err := io.ReadFull(rand, recipientNonce[:]); err != nil { return nil, fmt.Errorf("unable to generate recipient nonce for encryption") } // Pack recipient recipient := &containerv1.Recipient{ Identifier: identifier, Key: secretbox.Seal(recipientNonce[:], payloadKey[:], &recipientNonce, recipientKey), } // Return recipient return recipient, nil } func keyIdentifierFromDerivedKey(derivedKey *[encryptionKeySize]byte) ([]byte, error) { // Hash the derived key h, err := blake2b.New512([]byte("harp signcryption box key identifier")) if err != nil { return nil, fmt.Errorf("unable to generate recipient identifier hasher") } if _, err := h.Write(derivedKey[:]); err != nil { return nil, fmt.Errorf("unable to generate recipient identifier") } // Return 32 bytes trucanted hash. return h.Sum(nil)[0:keyIdentifierSize], nil } func tryRecipientKeys(derivedKey *[encryptionKeySize]byte, recipients []*containerv1.Recipient) ([]byte, error) { // Calculate recipient identifier identifier, err := keyIdentifierFromDerivedKey(derivedKey) if err != nil { return nil, fmt.Errorf("unable to generate identifier: %w", err) } // Find matching recipient for _, r := range recipients { // Check recipient identifiers if !security.SecureCompare(identifier, r.Identifier) { continue } var nonce [nonceSize]byte copy(nonce[:], r.Key[:nonceSize]) // Try to decrypt the secretbox with the derived key. payloadKey, isValid := secretbox.Open(nil, r.Key[nonceSize:], &nonce, derivedKey) if !isValid { return nil, fmt.Errorf("invalid recipient encryption key") } // Encryption key found, return no error. return payloadKey, nil } // No recipient found in list. return nil, fmt.Errorf("no recipient found") }