NEWS, EDITORIALS, REFERENCE

Lightweight Authentication Schema
This post is basically a white paper, but it's in a non-formal blog post style. I've been working on a new Application for C64 OS that makes use of networking. I've got a long list of Apps and Utilities that I'd love to have on the platform, but I have to prioritize the one I think will be most useful to have first.
For now, until I get further along and decide to share some details, I'm keeping the App a secret. But suffice it to say that, as with many online services, you need to authenticate yourself somehow and preferably share some account profile details, like your name and maybe your country, and perhaps your email address.
Authentication by a C64 OS Application or Utility to a network service is a tricky problem. It isn't just one problem but a collection of several different problems that all need to be solved in an elegant way. And it is that elegant solution that I've been daydreaming about. I think I have a pretty nice schema and I've been testing it with real code to make sure there aren't any snags in the implementation. Of course, it's not too late to change it if some good ideas for improvement come out of this.
The problems
- Memory in a C64 is limited for creating a username/password interface
- The C64's network connection is insecure
- Managing user profile data for multiple online services is tedious
- The various network proxy services need to be decentralized
Let's talk about these one at a time.
Memory constraints
Although C64 OS excels at making nice user interfaces, for both the mouse and keyboard, it is still a pain to:
- write the code
- load the classes
- layout the user interface
- store and retrieve the data
- test and debug everything...
...and so on. Doable, of course, but not something you revel in repeating across all your network-based Applications.
If we were writing an Application, then it's got an App bundle. That bundle could embed a Utility that's used just for entering your username, password and other profile details (more on profile details below.) But you also need to create a user account on the network service, so you need some variation in the form. You need a way to specify that you want to create a new account. You need to be able to try a username and password and somehow the system needs to be able to report back to you that the username is or is not available. The UI then needs messages for this and status labels.
If you're familiar with designing account creation systems for online services with an HTML form and javascript, it can be done, but it's a pain to get right even in a high-level language and environment like the web, as it has a few tricky subtleties. When I think about doing this in 6502, I find it a bit overwhelming. It is not what I would consider fun. It takes a lot of code and takes a lot of time to code and who wants to be doing that when you could be working on the real meat and potatoes of the service?
But it gets more difficult if the C64 OS program is itself a Utility rather than an Application. Apps have an app bundle and can store a Utility inside, and the Utility can offload the interface for managing the user credentials. When the App loads, it doesn't need to load the Utility too, it could just read in the settings file that the Utility saved from some earlier time when you used it to create the account. But what if our program is a Utility? Where to put all that form code now? A second Utility? That sucks. Utilties don't have bundles, so they'd have to be separately in the Utilities directory. And when you launch the one for configuring the account, it would have to close the one you're actually using.
And if you tried to cram the account creation and management code into the same Utility as the one that actually does the work of letting you use the online service, then it would be in memory all the time. It would be loaded from disk every time you launch the Utility. That sucks too.
Now, think, you have to do this work over and over and over for every different Utility you might want to write that connects to an online service that needs a user account. That's terrible. I don't want to spend my time writing code like this. Coding on the C64 is supposed to be fun, not tedious, unfun, work.
Insecure Network
I know that lots of stuff on the C64 is not particularly secure. For instance, there is no memory protection; any software can read and modify the memory allocated by any other app or process. The storage devices are wide open; there are no file permissions of any kind. Any file created by any program can be modified or even scratched by any other program. And any file that holds a username and password can be read by any other program. So, life on the C64 is the blissfully naive computing environment of the early 1980s when people did not think about this sort of thing.
In reality, the reason one program couldn't read and trash the files that belonged to a different program is because those other files were stored on other disks that were safely tucked away in a disk case somewhere. It stopped being quite so elegant when we started using mass storage devices, such as the CMD HD. But, hey, it is what it is and we have to make the best of it.
Networking, though, changes things again. Until and unless the networking hardware handles doing encrypted sockets, the C64 does not have the computational power to do the cryptography itself. And, even if some people start using hardware that can do encrypted sockets, what about the thousands of TCP/IP modems out there already which don't have this functionality? The point of C64 OS and its modular system of drivers and libraries is to be able to support different hardware devices. So we can't depend on or guarantee that the network hardware will encrypt the traffic.
There isn't a perfect solution for this, as far as I can figure. However, some scenarios are less optimal than others. For example, transmitting your password in the clear every time you open a Utility to use it, is categorically worse than some other scenarios where your password is not just sent out without some level of obfuscation. The form that this takes is part of what makes up the elegant solution we're discussing.
Managing Profile Data
This combines both of the preceding problems and adds another problem. It's a pain to write the code for managing your profile information, but then you have to do that for more than one App? That sounds like such little fun that I doubt anyone would bother.
Plus there is the insecurity of the network, so you're transmitting your personal profile information across the network? Better if you just didn't bother transmitting any profile info. But then, that kinda sucks in its own way. What if the service is about sharing and connecting with people?
The further problem though is that you have to manage your profile data multiple times. If I move house, and different network services have my physical address or my phone number, I've got to update those multiple times. Or if I get a new email address I have to maintain multiple profiles and keep them in sync. This is tedious too, who wants to do that? It's work.
Decentralization
This last one may feel a bit out of place, but the idea is that whatever happens, however this solution works, it can't depend on everything being hosted by services.c64os.com.
The CNP server code is open source, so anyone should be able to take it and host it on their own cloud infrastructure. At the same time, each network service has its own codebase (whether open or closed source) and must be deployable on different servers, under different domains, and managed by different owners. Despite this distribution function, the authentication system has to support mixing and matching of proxy servers, regardless of who hosts them or where.
Where your profile lives
The first thing that came to my mind is that I already have a database of all the C64 OS users. That database holds their name, email, address, and country as separate fields.
It also holds a username and a password hash that are used only for connecting to the CNP server. While it uses the email address and a separate password hash for logging into the account at:
https://www.c64os.com/c64os/account
Because I host a CNP server, and I have a database of C64 OS users, it made sense for me to create an account section of c64os.com where you can login and manage your CNP access credentials. This is done through a web browser over HTTPS. You can use your phone to securely log into the site and give yourself some CNP credentials. Later, the CNP credentials are stored in the network settings file on your C64 (in plaintext), and they are transmitted across the network, once, each time you start the CNP session.
Once the CNP session is rolling, accessing different network services, such as Wikipedia, or performing searches and downloads with Image Search, or using World Explorer, none of this is transmitting a password. You just have a connection open to the CNP server that stays open and is maintained with low-level, periodic, keep-alive messages.
Your CNP credentials are sent in the clear, but they're different from the credentials used for your account at c64os.com. If someone sniffs your CNP credentials, they cannot, for example, log in to see your purchase history or your CNP server usage history. Additionally, the only way to change your CNP credentials, or your other profile details, is by logging into your account at c64os.com. In other words, the only thing the CNP credentials do is let your C64 get online.
The CNP server code is written in NodeJS and it's open source. You can install your own instance of it wherever you like, but if you want your instance to host multiple users, with authenticated access, you have to provide your own user authentication system. You can create the MySQL database and store users in a users table, but you'd have to come up with your own system for maintaining that database.
I already have a database full of users, and I already have an account section of c64os.com with a web-based login system which lets you edit credentials and set up elements of your profile. Any profile details that aren't yet supported, I could easily add with minimal effort. For example, I could enable you to provide your phone number, a personal website address, a social media handle, etc.
This database holds basic, optional, profile data that you can configure in one place. What we need is a way to authenticate you as the owner of that profile and have the profile data be shareable, at a granular level, with third-party services that you want to gain access to.
What is an account?
Whenever you create an account somewhere, what do you do?
You want an account at timhortons.com? You specify that you want to create an account, you put in your name, you put in your email address, it asks you to confirm your email address usually by emailing a random confirmation token that you send back by clicking a link. Then you set up a password. And then you either forget what your password was, or you use the same password that you always use, or you let your browser remember the password for you, or if you're really ace, you use a password manager and save it in there.
Now what happens when you want to create an account with Spotify? It's the same procedure. Tell them your name and an email address, confirm your address, set up a password, save that somewhere. Then fill in some profile details, etc.
Next you want to create an account for the message forums at the Star Trek: The Customizable Card game's The Continuing Committee's website. Well, you give them a name and an email address. They send you a token with a link so you can confirm your email address. Then you set up a password. Then you configure your profile data.
It's the same thing. Over and over and over. And, yes there are services that provide authentication, such as Facebook, Google, Apple or Microsoft, but they're not universally (or even widely) accepted. And what's worse is that Facebook and Google generally provide the service because they want to track your movements around the internet. Then there's OpenID, but it's super nerdy, and it's even less widely accepted than those provided by one of the big players.
The other detail we're glazing over here in these examples is that none of this works from the C64. We won't be using OpenID for Facebook for authentication with a Commodore 64.
The central question is, what is an account?
An account is a record owned by a service that uniquely represents you. All of your activity with that service is related to that record that represents you. Metadata or profile data is information about you, rather than information generated by your usage of the service, but it's still attached to or related to your account record.
Identification is the process by which you indicate who you are and thus claim an account record as yours.
Authentication is the process whereby you prove that you are who you've said you are.
And if you do both of these things then you gain access and take control of that account. This usually takes the form of a username and password. The username is unique and it identifies you. Your knowledge of the secret password authenticates that you are the owner of that identifier. This is the traditional way to do it, though not necessarily the best, or the most secure, or even the most convenient.
We old fogeys remember this from the days of BBSes. You dial up the BBS. It asks you to identify yourself, usually with a username, though sometimes an auto-assigned user number, and after that to authenticate yourself with a password. Very straightforward.
And what happens if you don't have an account with this BBS? You have an option at the prompt to indicate that you're a new user, that you want to create an account. It lets you pick a username. It gives you an error if it's already taken (although now you know someone else's username.) Then you give it a password. Old systems like BBSes didn't usually enforce strong passwords, so a password like "god", "sex", "money", "love" or "12345" became the trope of old-school hacking. After specifying your password it usually asked you a few questions about yourself: name, age, sex, city, country, maybe a phone number; i.e., profile data. And everything was stored in that BBS's database, which might have been a flat file or a REL file or whatever.
What happens now when you want to create an account on another cool BBS you just learned about? You go through exactly the same process, (although with potentially a radically different look and feel to the interface, since every BBS is unique.) And unless you're rain-man, you don't want to remember a separate username and password for each BBS, so you either write them on a piece of paper (and hope you never lose or misplace it,) or more likely you choose the same username and password for every BBS. Plus you put in the same profile information for each one... and lo and behold this is precisely the onerous situation which we are today trying to avoid.
The only convenient thing about BBS account creation is that BBSes by their very nature are raw, back and forth, question and answer, data streams. On the client side it takes zero extra programming to handle account creation or authentication. Creating an account or logging into an account, from the perspective of a terminal emulator, are precisely identical to the way in which one just uses a BBS.
Auto-account creation. Why not?
It occurs to me that the process of logging in and the process of creating a new account are closely related. If I connect to a service and I want to login but I can't because I don't have an account, I'm going to create an account with the credentials that I want and then use those credentials to login. Logging into any service for the first time and creating the account are virtually identical, but for the inconvenient though necessary steps taken to create the account.
The only tricky part is ensuring that the part that identifies you, typically the username, is unique. It's almost as though you could just roll account creation into the process of logging in. You login as: jsmith,gimmeaccess123 And if there is no "jsmith" account it could create a new account identified by "jsmith" and configure its initial password to "gimmeaccess123".
Almost. But this would lead to problems. For example, clashes of ID. If "jsmith" is already taken, should it tell you, I'm sorry "jsmith" account is already taken, or should it tell you, "Hi there, jsmith, your password is not correct." This would be confusing. Another potential problem with this is when "jsmith" tries to log into the account he already has, but accidentally logs in as: jsmth,gimmeaccess123. Oops, now it goes ahead and creates a new account called "jsmth" (note: the "i" is missing.)
There is one other problem that I can think of. Typically when creating an account you are asked to put your password in twice, to make sure you typed it without mistake. If your account were created instantly upon initial login attempt with an otherwise unknown username, you might have a typo in your password. You log out and suddenly you can't log back in.
Besides these catches, it really is almost possible! It's so close, but there are those problems mentioned above, and therefore it just doesn't quite work that seamlessly. Is there someway to overcome these problems?
Unique Identification
Then my thinking took me to the idea of a UUID. A UUID is a Universally Unique IDentifier. It's a long number with cryptographically random elements, and parts that are based upon increments of real time. The result is that everyone, anywhere on earth, can generated loads and loads of UUIDs all day long, totally independently, and yet the probability that any two UUIDs are the same, now, or with any past UUID or with any UUID that will be produced in the future, is vanishingly small.
If your account identifier were a genuine UUID, generated just for this account, then you could virtually guarantee that it would not clash, and that problem would be eliminated. Of course, you have another problem. UUIDs are even longer and harder to remember than a username. And somehow the system would need some way to guarantee that any UUID that's never been in the system before is an intentional attempt to create a new account.
We can come back to this though.
BBSes were isolated islands of telecommunication. Typically when you dialed in you occupied 100% of its bandwidth. This is not the case with modern socket-based network services. (It's not technically the case with modern BBSes either.) When we open a socket connection to a network service, that service itself, perhaps written in NodeJS, can make other connections to other services on the internet without any problem.
So let's start putting these things together.
CNP Authentication Service
If you're connected to the internet with C64 OS, then you are already connected to a CNP server. You've already identified and authenticated yourself to that server.
The idea is, we add to the CNP server an authentication service. And then other services that need users to identify and authenticate themselves, and possibly to provide profile information, can be written to use the CNP authentication service.
The general idea is as follows: The C64 OS Application that wants to log into a service first asks its own CNP server to generate an authentication token. The C64 OS Application then opens a connection to the remote service, but instead of passing it a username and password, it passes it the token. That service then connects to the CNP server directly, and passes it the token in order to fetch information about who is trying to login.
That's the idea in a nutshell.
Let's first walk through the complete protocol, and then talk about the problems it solves.
CNP Authentication Protocol
To open a new CNP socket, the CNP library in C64 OS sends a socket open-type packet. This includes a socket number, a data size and checksum, and the payload is the remote host and port to bridge to.
The remote connection is specified as a string, either:
domainname:port or ipaddress:port
There are always two parts separated by a colon. A new feature of the CNP server is the ability to open a socket to a service that's built-in to the CNP server itself. From the perspective of the Commodore Network Protocol, it behaves just like a CNP socket, except instead of bridging to a TCP/IP socket connection to something remote it gives the C64 a socket connection to something within the CNP server. The format is the following:
:service:parameters
The connection string as a whole is identified as being a local service rather than a remote TCP/IP bridge because it starts with a colon. Following the first colon is a service name. And following the second colon is a string of parameters. The format of the parameters string depends on the service.
The authentication service, therefore, I propose to be "auth". Thus, the C64 opens a CNP socket with the connection string:
:auth:parameters
And you get a CNP socket connection to the authentication service of the CNP server that you're presently connected to.
Now what about the parameters? I can think of at least two "security devices" that would be useful to have in the token. The first is that the token is being issued for a specific third-party service. For example, if you're trying to access a message forum service then you should be able to create an auth token that can only be cashed in by something claiming to be that service.
Additionally, an auth token shouldn't just last forever. It should expire after some short but configurable amount of time. Neither of these "security devices" is detectable from the token itself. I.e., there is no way from the token itself to know what service name may be used to cash it in or for how long it will remain valid.
Besides these security devices, which relate to how to cash in a token, there is also what the token authorizes you to retrieve from the auth server. Most fundamentally you get a UUID that identifies the user who generated the token. This UUID is unique not just to the user but to the user + service name pair. We would also like to be able to specify profile info that this token grants you access to. Because the database which backends the list of C64 OS users already stores, name, email, address and country, these seem like obvious first choices. Additional profile details can be added later.
The parameters are a simple comma-delimited list. There is no limit on the number of characters in a parameter, but there is an upper limit on the total length of the connection string which is determined by the CNP protocol as 256 bytes, which is the maximum payload size of the open-type packet. The only character not permitted in a parameter is a comma, since that's used to delimit parameters.
The first parameter is always the service name. The second parameter is always the token's time-to-live in seconds. Minimum value 1 = 1 second, and there is no practical upper limit. 300 = 5 minutes, 3600 = 1 hour, etc. Recommended duration is around 10 seconds which should be enough time for even the slowest connection. All parameters that follow are authorization to profile details. For brevity I've defined single letter codes for the initial 4 parameters: n = name, e = email, a = address, c = country. These are not limited to one character, of course, so there is no limitation in the future. If you wanted a property for nickname it could be defined as "nn" or "nick" etc.
Putting it together
Putting this together then, the following is how we authenticate with a third party service that uses the CNP Authentication Protocol (CAP?).
First, we're already authenticated with our CNP server. We open an App that wants to authenticate with a third-party service. The App opens a CNP socket with the following connection string:
:auth:superforums,10,n,e,c
The CNP server sees this connection string is for a local service, the "auth" service. It creates an authtoken object which it associates with your current CNP session. The authtoken object generates for itself a random token string. And the object is configured with the properties you've supplied: The service name is "superforums", the time-to-live is 10 seconds, so it sets an expiry timestamp that is now + 10 seconds. And it stores the properties which it is authorized to give up: n, e and c for name, email and country.
The CNP socket on the C64 receives the token string and the remote end closes that socket connection. The socket close event can be used as the trigger to proceed to the next step. Our App opens another CNP socket, this time to the third-party service. The connection string is the TCP/IP domain:port where the service is hosted. When this socket opens, the App transmits the token string followed by a carriage return ($13 character), to mark the end of the token, and leaves the socket open.
At this point, the third-party service uses the token to perform the identification and the authorization, and to fetch profile info. The service can then do whatever it wants. If the authentication fails, it can send an error and close the socket. If the authentication succeeds then it can proceed following its own communications protocol, which of course the App has been written to speak.
How does the third-party service use the token?
You may have noticed that I've glazed over some details. The above section describes how the Application on the C64 uses the CNP Authentication Protocol, and you should see that it is very easy. One of the main purposes is to make it very easy on the C64-side. But how does this actually work between the CNP server and the third-party service?
To support the CNP Authentication Service, the CNP server now opens a second TCP/IP listening socket. The first socket is the standard one that a computer running C64 OS connects to, to get online. The domain name and port are saved in the C64's network settings file. This may be something like services.c64os.com:6400. The new second listening socket is on a different port, and is a TLS encrypted socket. This can be any other port, like, say, 5502, or something. When the CNP server is configured it knows the domain name and port number which can be used by remote services to make connections to it.
When the C64 asks the CNP server to create a new auth token, the authtoken object generates a random 24-character alphanumeric token. This part is purely random with no structure. But what it sends to the C64 is not exactly this. It takes the domainname:port for its TLS listening socket, and symmetrically encrypts that using the random token as the key. Then it encodes the cipher text in base62 which is precisely the same character pool as the random token.
abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 0123456789
It then appends the random 24-character token to the end of the base62-encoded cipher text, and that is what it returns back to the C64. The C64 doesn't do anything with it except send it (plus a carriage return) to the third-party service.
The third-party service receives the token, and knows when it's got the whole thing because it's terminated by a carriage return. It then slices the last 24 characters off the token, base62 decodes the remaining string, and then symmetrically decrypts that using the 24-character token as the key. Now it has the domainname:port for the CNP authentication server.
It opens a TCP/IP socket, TLS encrypted, to the CNP authentication server, and it transmits two lines, each terminated by a carriage return. First it sends its own service name. It cannot determine the service name from the token itself, but it knows its own service name, so it sends it, then it sends the 24-character token, like this:
superforums{CR} L7kpwknYtgQaBEKFJHPnfYz4{CR}
The CNP authentication server, although it's on a different port, shares its NodeJS memory space from the standard CNP server to which C64 OS clients are connected.
The CNP authentication server first removes all expired authtoken objects. Then it searches through the remaining ones looking for one that matches both the service name and the 24-character token. Note also, that if a C64 OS client ends its CNP session (or drops the connection), all authtoken objects associated with that session are immediately destroyed, regardless of whether they have expired or not.
If a matching authtoken object cannot be found, it returns an error and closes the connection. An error will subsequently be propagated from the third-party service back to the C64, which can re-initialize the whole procedure, or give the user a retry button, or an error message, or whatever.
On the other hand, if a matching authtoken object is found, then it proceeds. The authtoken object is attached to an active CNP session, which holds a CNP userRecord. It looks up, in an authids table, a record for this CNP userid and this service name. If there isn't one of those yet, it generates a UUID that uniquely identifies this CNP userid for the service name. The protcol calls this UUID a "ForeverID". The new ForeverID is saved in the authids table for this userid + servicename, and is then returned as the first line, terminated by a carriage return.
If the authtoken object has been configured to authorize access to some profile info, the profile info is returned on subsequent lines, as key:value pairs, each line ending with a carriage return. Followed by a single blank line to end the auth response, and then it closes the connection. So, a response to the Super Forums request which it made with our valid auth token (within the 10 seconds it was valid for), will come back as something like this:
965b8b3c-056f-47c5-a8d1-31025fef7dd6{CR} name:Gregory Nacu{CR} email:gnacu@wherever.com{CR} country:Canada{CR} {CR}
The Super Forums service (this is just our made-up example service), gets the ForeverID as the unique identifier for the user. It could in theory use this as the working ID for this user, but that's not recommended. It should have its own users table with a standard auto-incrementing userid, with a foreverid field that is associated with the user record.
What happens if there is no user record with this ForeverID? Well, it should create a new user record for this ForeverID, and it can populate the profile info with whatever it happens to get and wants to keep. For example, Super Forums might retain the mame and the email address, but ignore the country. If the authtoken didn't authorize it for either of these, it has the right to return an error to the C64 OS Application. In practice though, the C64 OS Application will request the generation of an auth token with authorized access to the profile info that the service requires.
That handles creating a new account. Now what happens the next time you launch this C64 OS Application and it tries to authenticate? It makes another auth request to the CNP Server, like this:
:auth:superforums,10,n,e,c
It gets back a new token, which looks totally random and is completely different from the previous one. It sends that up to the third-party service, which decodes it as described above. It decodes probably the same domainname:port as the last time, though not necessarily. And it's a completely different 24-character token. It opens the TLS-encrypted socket to that domainname:port and sends the service name and the token, just as before.
Imagine now though, that between these two authentication procedures, you've logged into your account at c64os.com, and you've changed your name from "Gregory Nacu" to "Greg Nacu", and you've changed your email address from "gnacu@wherever.com" to "gregnacu@somewherenew.org". The CNP auth server finds the matching authtoken object, it looks up the ForeverID in its authids table, finds the existing one for this CNP userid and service name, and returns it. Followed by the profile info this token is authorized to send back. This time it looks something like this:
965b8b3c-056f-47c5-a8d1-31025fef7dd6{CR} name:Greg Nacu{CR} email:gregnacu@somewherenew.org{CR} country:Canada{CR} {CR}
It's the same ForeverID. So it authenticates our access to the same user record at the Super Forums service. But Super Forums can also update its user record with the current name and email address provided from the CNP auth server, thus updating the profile info automatically.
Things to consider
Decentralization
It is in fact decentralized. We can imagine that Super Forums is hosted at superforums.com. That domain could have a nice webpage for the public to read about, and it could have its custom binary protocol listening on port 1200 or something. The CNP Authentication Server could be on services.c64os.com:5500. Not only that, but different users could have different CNP Authentication Servers. When a client opens a socket connection and passes in a token, that token could encode anywhere for the CNP auth server. And from the 24-character token, it could be any user, it won't know who it is until it sends it to the CNP auth server and gets back a ForeverID.
The ForeverIDs are UUIDs. So if one user is using services.c64os.com for his auth server and another user is using c64os.skynet.org for his, they both independently generated a ForeverID for their users, and Super Forums integrates them both and because they're UUIDs they will not collide.
Security
Is this actually secure? No, technically this is not secure. But is it more secure than if the C64 OS Application were sending its username and password over a plaintext socket? Yes! It's much more secure.
- The token only lasts a short period, say, 10 seconds. After which, this particular token is useless.
- Where exactly this token is supposed to be sent is obscured by the fact that the CNP server's domainname:port are encrypted within the token. This is only an obscuration, since, the symmetric decryption key is part of the overall token. But to the casual observer, it just looks like a random string. Even its length is not fixed.
- The service name for which this token is valid is not included in the token. The service name was sent in a different payload to a different target (the CNP auth server).
- When the third-party service opens a connection to the CNP auth server, and sends the service name and the 24-character auth token, this is over a TLS-encrypted socket.
- The ForeverID is never transmitted over an insecure socket. The ForeverID is never sent to the C64. In fact, the C64 OS Application has no idea what its own ForeverID is. Only the service itself gets the ForeverID, which it gets over the TLS-encrypted socket. The service never sends the ForeverID anywhere, it's used only for connecting the open socket it has from the C64 with an internal user record.
- Even though Super Forums knows the ForeverID for this user for its own service, this same user has a different ForeverID for every different service. And the only auth tokens that are ever sent from the C64 to Super Forums are ones which have been authorized for the "superforums" service name.
All that said, it's not technically secure. Technically, if someone knows and understands the protocol, and they can sniff your packets, they can intercept the :auth:... request, and get from that the service name. Then can then sniff the incoming response packet in order to get the auth token. They could then, using a program because they only have 10 seconds, split the 24-byte key off the end, decrypt the first half, use that to open a secure socket connection, and then send the service name they sniffed from the :auth:... request, followed by the 24-byte token, and they'd get back your ForeverID.
Now that they have your ForeverID, they can create their own fake CNP auth server. Generate a token that embeds the domainname:port of their own auth server, open a connection to the Super Forums server, transmit that auth token. Let the Super Forums service decode the token, and open a connection to their fake auth server, to which they can simply return your ForeverID, and boom, now they've successfully spoofed being you.
So, is it secure? No, it is not technically secure. But the above is a lot of work. And what they get from it is access to your Super Forums account. They'd have to do that again to get your ForeverID for each service. If we really dig in, your CNP credentials are also going over the clear. And they could sniff your CNP credentials, then log into your CNP server as you and just request an auth token. And so on. BUT...
Convenience beyond security
From the start of this blog post, I tried to make a case that there are numerous problems we're trying to solve. This solution goes beyond just trying to be more secure.
The initial problem is, the tediousness, the memory constraints, and the general inconvenience of having to write user interface code that manages letting the user input and store his username and password, especially in a Utility that has even more constraints than an Application.
CNP Authentication Protocol solves this admirably. It takes very little code to set this up in a C64 OS Application or Utility. It's just a snippet that you can paste into your code and a string for the auth connection, like ":auth:servname,10,n,e". After a call to open the socket, the response token comes back in a single packet, so it's super easy to read and store in a temporary one-page buffer. And then when you open the socket to actually communicate with the service, literally the only thing you need to do is write the buffer contents to the socket when it opens, and you're done.
Another problem is managing your profile data. With CNP Authentication Protocol, your profile data is stored in one central location. You can change your name or email address, etc., in your CNP account profile, and the very next time you authenticate with some service, boom, it gets your updated info. This is one-way, of course, too. So, some service like Super Forums cannot push a name change back into the CNP auth server. But it can read changes to your profile from the auth server.
Account creation was another problem. The back and forth of account creation is notoriously picky, and takes lots of code and tedious programming to do it right. You'd love to just roll logging in and account creation into the same process, but a simple typo of your username makes it ambiguous whether you're trying to make a new account or made a mistake logging into an existing account. The way the CNP Authentication Protocol works, these kinds of mistakes are impossible. You either send a valid token or not. When the token is sent to the auth server, it gives you a ForeverID that belongs to the user, who's signed into this CNP session. There is no opportunity for a mistake about whether you're signing into an existing account or trying to create a new one.
It meets the criteria of decentralization. It's even better in fact. If your name is John Smith, and you sign into a BBS for the first time and you want to set up a new account, and you say, I want my username to be "jsmith", you instantly get bumped up against "Jane Smith" who's already created her account with that name. With the CNP Authentication Protocol, this never happens. Each of you, even if you have the same name, is identified by your UUID-based ForeverID.
And there is more to security than just an encrypted network socket. You'll never be inclined to use the same username and password with multiple services simply because it's easier to remember them all. And the people who run Super Forums don't have access to your username and password, which they could then turn around and try poking into every other service hoping that you're not security conscious. Unless the people at Super Forums are in a position to intercept all your network traffic, having your ForeverID for their service does not help them gain access to your accounts at other services.
This also means that a security leak at Super Forums, even if their complete database becomes public, cannot expose your username and password. Even the most insecure practices of some rando service cannot leak what they don't have.
And lastly, it is objectively more secure that a random-looking string passes through the unencrypted network, (which is emphemeral, lasting for just 10 seconds before becoming invalid and never to repeat), than it is to pass the same static username and password through the unencrypted network which a person can literally interpret with their eyes and easily remember. Like, which is more secure, seeing this flow through the network (one time only, never repeated):
DwCxiaB90KSz1Hdwt0d7s942v3CpeRVw55CN0QxFmZg0oXtAa8MiEgsdgZwwl
Or seeing this pass through the network (over and over and over again) ?
gregnacu Corky4Doodle
I think we have a winner. Hit me up in the comments or in the Developer channels on the C64 OS Discord if you have thoughts or suggestions for improvement.
Do you like what you see?
You've just read one of my high-quality, long-form, weblog posts, for free! First, thank you for your interest, it makes producing this content feel worthwhile. I love to hear your input and feedback in the forums below. And I do my best to answer every question.
I'm creating C64 OS and documenting my progress along the way, to give something to you and contribute to the Commodore community. Please consider purchasing one of the items I am currently offering or making a small donation, to help me continue to bring you updates, in-depth technical discussions and programming reference. Your generous support is greatly appreciated.
Greg Naçu — C64OS.com