func()

in yubikey/pkg/value/encryption/envelope/piv/service.go [56:109]


func (s *service) Decrypt(ctx context.Context, encrypted []byte) ([]byte, error) {
	// Extract envelope
	var r response
	if err := cbor.Unmarshal(encrypted, &r); err != nil {
		return nil, fmt.Errorf("unable to decode envelope: %w", err)
	}

	// Extract public keys
	pivPublicKey := s.card.Public()
	pivCompressed := elliptic.MarshalCompressed(pivPublicKey.Curve, pivPublicKey.X, pivPublicKey.Y)

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

	// Compare tag
	if !security.SecureCompare(tag[:4], r.Tag) {
		return nil, errors.New("invalid identity tag")
	}

	// Extract ephemeral public key
	x, y := elliptic.UnmarshalCompressed(pivPublicKey.Curve, r.EphCompressedPublic)
	if x == nil {
		return nil, errors.New("cannot unmarshal ephemeral public key")
	}
	ephPub := &ecdsa.PublicKey{
		Curve: pivPublicKey.Curve,
		X:     x,
		Y:     y,
	}

	// Compute shared secret
	sharedSecret, err := s.card.SharedKey(ephPub, s.prompt)
	if err != nil {
		return nil, fmt.Errorf("unable to compute shared secret: %w", err)
	}

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

	// Decrypt
	// Use fixed nonce to save space and also sharedsecret is derived from ephemeral
	// key that act as nonce.
	nonce := make([]byte, chacha20poly1305.NonceSize)
	clearText, err := aead.Open(nil, nonce, r.Payload, nil)
	if err != nil {
		return nil, fmt.Errorf("unable to decrypt payload: %w", err)
	}

	// No error
	return clearText, nil
}