package engine import ( "crypto/ecdh" "errors" "fmt" "golang.org/x/crypto/chacha20poly1305" ) func (e *Engine) Decrypt(recipientPriv *ecdh.PrivateKey, senderPub *ecdh.PublicKey, encryptedMsg []byte) ([]byte, error) { if len(encryptedMsg) < PubKeySize+NonceSize { return nil, errors.New("message too short") } // Unpack ephemeralPubBytes := encryptedMsg[:PubKeySize] nonce := encryptedMsg[PubKeySize : PubKeySize+NonceSize] ciphertext := encryptedMsg[PubKeySize+NonceSize:] ephemeralPub, err := e.curve.NewPublicKey(ephemeralPubBytes) if err != nil { return nil, fmt.Errorf("invalid ephemeral public key: %w", err) } // Reconstruct secrets ss1, err := recipientPriv.ECDH(ephemeralPub) if err != nil { return nil, fmt.Errorf("ecdh ss1 failed: %w", err) } ss2, err := recipientPriv.ECDH(senderPub) if err != nil { return nil, fmt.Errorf("ecdh ss2 failed: %w", err) } // Derive key symmetricKey, err := deriveKey(ss1, ss2, nonce) if err != nil { return nil, err } aead, err := chacha20poly1305.NewX(symmetricKey) if err != nil { return nil, fmt.Errorf("failed to create aead: %w", err) } // Bind ephemeral and sender public keys to ciphertext via AAD aad := buildAAD(ephemeralPubBytes, senderPub.Bytes()) return aead.Open(nil, nonce, ciphertext, aad) }