func()

in yubikey/pkg/value/encryption/envelope/piv/service.go [111:153]


func (s *service) Encrypt(ctx context.Context, cleartext []byte) ([]byte, error) {
	// Extract public keys
	pivPublicKey := s.card.Public()
	pivCompressed := elliptic.MarshalCompressed(pivPublicKey.Curve, pivPublicKey.X, pivPublicKey.Y)

	// Identity tag
	tag := sha256.Sum256(pivCompressed)

	// Generate ephemeral key
	eph, err := ecdsa.GenerateKey(pivPublicKey.Curve, rand.Reader)
	if err != nil {
		return nil, fmt.Errorf("unable to generate ephemeral key pair: %w", err)
	}
	ephCompressed := elliptic.MarshalCompressed(eph.Curve, eph.PublicKey.X, eph.PublicKey.Y)

	// ECDH shared secret between ephemeral key and yubikey
	sharedSecretNum, _ := eph.PublicKey.ScalarMult(s.card.Public().X, s.card.Public().Y, eph.D.Bytes())
	sharedSecret := sharedSecretNum.Bytes()

	// Derive AEAD cipher from parameters
	aead, err := s.deriveAEAD(ephCompressed, pivCompressed, sharedSecret, chacha20poly1305.KeySize)
	if err != nil {
		return nil, fmt.Errorf("unable to initialize aead: %w", err)
	}

	// Encrypt
	// Use fixed nonce to save space and also sharedsecret is derived from ephemeral
	// key that act as nonce.
	nonce := make([]byte, chacha20poly1305.NonceSize)

	// Return encrypted content
	body, err := cbor.Marshal(response{
		EphCompressedPublic: ephCompressed,
		Tag:                 tag[:4],
		Payload:             aead.Seal(nil, nonce, cleartext, nil),
	})
	if err != nil {
		return nil, fmt.Errorf("unable to encode envelope: %w", err)
	}

	// No error
	return body, nil
}