Data availability + processing : Storage
As per EIP-2229 and assuming that you are setting a value to storage keys not previously assigned the cost per SSTORE would be : 20000
(SSTORE) + 2210
(SETTING NEW KEY) so an increase of 10.5% to your information for SSTORE gas cost.
For 1Kb : 32 SSTORE operations are required so : 710,720
gas.
For 20 kb : 14,214,400
gas
Assuming a gas cost of 252
Gwei, that's a total gas cost of : 3.58
ETH.
You were not taking EIP-2229 into consideration, but your computation is fine.
The result I'm getting is telling me it would only cost 3.23 ETH to
store 20KB of plaintext in a transaction, but I'm having a hard time
believing this.
Me too, but not for the same reason as you apparently. This is 12,920
USD pre EIP-2229, and 14,320
USD now. That's a huge amount of money for such little amount of data.
Only data availability : Calldata
If all you are only interested in data availability and don't need to process that stored data on chain in any way, you can take inspiration from the way Rollups store data to L1: using calldata instead of storage.
The cost of calldata is currently of 16 gas per byte (EIP-2028) and might very well be reduced with EIP-4448.
Just taking the current calldata cost for 20Kb that's : 16 * 1024 * 20
= 327,680
gas, assuming the same 252
Gwei gas price that's "only" 0.082
ETH or around 328
USD. (I'm taking the current ETH price : 4000 USD)
Which is obviously much better.