You can run your own private one at home with a Raspberry Pi/ttl serial adapter and a cheap SIM module. Or use a dongle.
I don't understand why Google Authenticator is supposed to be so conceptually different from a password. The codes it provides are a deterministic (and public!) function of a code shared between you and the party you're authenticating to. Or in other words, it's exactly equivalent to a password.
When I set up 2FA on github, they gave me a code to put into Google Authenticator, and also a bunch of "one-time reset codes" in case I lost my phone. Why wouldn't I just record the original code in whatever place I'm supposed to store the one-time reset codes?
1. Not shared across sites 2. Guaranteed minimum length 3. The portion going over the wire cannot be used more than once or after a few seconds 4. The phone app doesn't expose the seed or allow you to copy it off the device. Running on iOS has strong guarantees against access by other apps.
That's not perfect and U2F is a huge improvement but it's safer than SMS and given the number of google users who get attacked daily a reasonable incremental improvement.
1. Passwords are selected by the user; you cannot prevent reuse. While it's technically possible that a site could allow you to set the TOTP seed nobody does.
2. Password entropy is notoriously hard to calculate programmatically – e.g. well known movie passwords or leet-speak are often judged as stronger than shorter true-random sequences – but you generate the TOTP seed.
4. As with #1, preventing reuse is hard. Knowing that all extent implementations offer fairly strong protections against accidents is a key difference.
Your finally point depends on how you judge the whole system: “something you know" can be told to someone else, which isn't true of TOTP. It's also always valid and reusable whereas the one-time code is closer to an on-demand verification. From the perspective of security boundaries, a desktop user accessing a separate token, phone, or arguably even an iOS user using a separate app is closer to something you have than something you know.
TOTP isn't the strongest form of MFA but it's closer to that end of the spectrum and, especially in consumer contexts, a huge improvement over nothing. In the real world, those kinds of security improvements are still meaningful even if they're not perfect implementations of a textbook concept.
This is not correct. When I set up TOTP 2FA, the site generates a seed and tells it to me. It is always valid and always reusable, and it's no more difficult for me to communicate it to anyone else than it was for the site to communicate it to me.
Think of the physical one-time tokens: If I know the data stored on it, I can replicate them too. So, are they "something you have", or "something you know"? The reason why they're "something you have" is that it's easier to have the physical ownership of the device, than to know they "secret" of the device. But, the same is true about Google Authenticator! In that respect, Google Authenticator can be, indeed, interpreted as "something you have".
If you're building a consumer service, that's a big improvement over someone picking their dog's name and saving it in a Word doc on their desktop.
hash_fn(concat(IV, time)) -> func(somehash) -> [code seq. 1]
Then on the next round the IV is discarded and it becomes:
hash_fn(concat([code seq. 1], time)) -> func(somehash) -> [code seq. 2]
That's almost certainly an over-simplification / wrong; it doesn't seem very secure to just use the previous output as the IV for the next round. Maybe it chains all previous outputs together somehow, IDK. I should probably research this, given how much I rely on 2FA for security.
EDIT: The Wikipedia article on TOTP seems to suggest that you're right; the initial shared secret looks indistinguishable from a password (although the article is a bit vague): https://en.wikipedia.org/wiki/Time-based_One-time_Password_A...
You can then set the service up to forward received SMS messages to your regular SMS number—but, if your phone is compromised/stolen, you can go back to the account and immediately turn off this forwarding.
---
Sadly, this approach reduces the security back to single-factor, since you get into the SMS account with something you know, rather than something you have.
You can at least treat the VoIP account like a password-manager: give it a long, unforgiving "master password." (Or a random password that's stored in a password-manager, if you use one.)