cx7 - Open-Source Cross-Platform One-Time Pad Command-Line Interface Tool


1. About cx7

cx7 is an open-source cross-platform one-time pad command-line interface tool. It offers the necessary functionality to generate cryptographic one-time pads, en- and decrypt binary data with one-time pads, establish peer-2-peer connections via TCP sockets, manage text and data transfer of encrypted data and provides a basic key management. The server version provides a simple DNS management for cx7's socket connections.

1.1 Concept

cx7 is written for the ruby environment and can therefore be run on every platform ruby supports. The interpreter language as well as the plain procedural program design allows an easy altering for specific needs. The simple core functionality is collected in a small function library and can be easily included in other ruby programs.

1.2 Limitations

First at all, cx7 is not developed to offer more than a rudimentary usabilty. Its pure aim is security. At this time, there are some limitations: For en- and decryption, cx7 workes byte-wise and loads the whole data into the memory. Therefore a limit is the physical memory of the processing device (this may change in future, cf. 5.1 Speed). Furthermore cx7's socket communication is designed for one-on-one communication only. The socket communication of the cx7 protocol is based on TCP. Therefore cx7 is not suitable for real-time communication. This is mainly due to the nature of OTP, which demands a precise control of the transferred bits to keep the key in-sync. Drop-packets like in UDP would be fatal for a key management. In case the bit-stream is fast enough and exceeds the bit-rate of an transported data-stream, it may be possible to establish a pseudo real-time communication, a.k.a pseudo-streaming or progressive download.

2. Download

2.1 cx7-nix

2.1.1 Download latest version | 6951 bytes

2.1.2 Changelog


2.1.3 Checksum

MD5 (128b\%x): 3c963db10315d007de4159fe23a6d508

2.2 cx7-server

2.2.1 Download latest version | 2144 bytes

2.2.2 Changelog


2.2.3 Checksum

MD5 (128b\%x): 908123624c5a729d15aceea20cd26c85

3. Documentation

3.1 Installation

First install the ruby environment on your machine. cx7-nix was sucessfully tested down to ruby >= 1.9, cx7-server even to ruby >= 1.8. Then download the archive above and extract it to your desired location.

Depending on your OS security setup, you may need administrative rights to access the local file system, bind ports or connect to higher ports. It is strongly recommended to not give cx7 root rights or full administrator rights. On n*x platforms the best way seems to be to set up an own user/group for cx7 and limit its rights to seperate disk-space and specific ports.

3.2 Starting cx7-nix

You start the program with the following command: ruby /path/to/cx7.rb where "/path/to/" is the path you extracted the archive to. You'll get a STDOUT like: # cx7 0.1.5-alpha # Starting thread for STDIN ... OK # ready ... (type 'help' for help) With an threaded STDIN at the end.

3.3 Command list

Typing "help" (and pressing enter) gives you a command list like this: # syntax: # [command] [paramter1] ([optional parameter2]) ([...]) # commands: # clr|c [message] - send a not encrypted MESSAGE # connect|con ([ip] [port] | [id]) - connect to IP on PORT or ID # exit|quit|q - leave program # file|f [path/filename] - send a FILE to connected peer # generatekey|gk [path/filename] ([size in bytes]) - generate a key on PATH with SIZE in bytes # help|?|h - show this help # identify|id [id] [password] ([port]) ([ip]) - identify at ident server with ID and PASSWORD; optional: enforce PORT and/or IP # keyposition|kp ([key]) - show the currect position of KEY # localdecrypt|ld [path/infile] [path/outfile] ([key]) - decrypt a local FILE with KEY # localencrypt|le [path/infile] [path/outfile] ([key]) - encrpyt a local FILE with KEY # localswitch [path/infile] ([path/outfile]) ([key]) - switch a local file to a new file # msg|m [message] - send an encrypted MESSAGE # serve ([port]) ([ip]) - start a server on PORT and bind IP # setidentserver|sid [ip/host] [port] - set IP/HOST and PORT for ident server # setkey|sk [key] - set the KEY to use # stat [id] - get status information about ID # sync - send current key position to connected peer # unconnect|uc - end connection # unserve|us - stop server # updateposition|up [key position] ([key]) - update KEY to specific POSITION # notes: # - whitespaces in path-names can be escaped with \

3.3.1 Syntax

cx7 uses a basic syntax. A command followed by parameters. Some parameters are required and some are optional. Command and parameters are seperated by whitespaces. To use whitespaces as part of a parameter, you can escape these whitespaces with "\". You don't need to escape whitespaces in messages though. See examples below.

3.3.2 # clr

After establishing a connection to a peer, the command clr (abr. clear) sends an unencrypted message to this peer. The message is clear text and does not use any key.
Alternatively type "c". clr Hello, this is an unencrypted message.

3.3.3 # connect

Establish a connection to a hosting peer while acting as a guest. There are two options what follows as a parameter: either an IP-address and a port-number or a identification string which was registered at a running and connected cx7-server. To connect to an IP-address type: connect 7171 where "" is the reachable IP-address of your peer and "7171" is the port to which the remote cx7 host is listening to. To connect with an identification string type: connect AB01 where "AB01" is the registered identification string.
Alternatively type "con".

3.3.4 # exit

This command closes all connections, ends all threads and leaves the program.
Alternatively type "quit" or "q". exit

3.3.5 # file

After a connection to a peer has been established, the command file starts an encrypted data transfer to this peer.
Alternatively type "f". file /path/to/ where "/path/to/" is the path to the file and "" the file itself. If the file is accessible cx7 sends a request to the connected peer: [| # Accept file (yes/no) In case the peer accepts the transfer, the file gets encrypted and transferred, then decrypted and stored at the peer's machine. On the peer's machine the file gets stored in the working directory. (Remember that the working directory is not necessarily the directory cx7 remains.)

3.3.6 # generatekey

This command generates an OTP key. generatekey /path/to/key.cx7 1024 where "/path/to/" is the directory the key should be stored in and "key.cx7" the filename of the key. The second parameter is the wished size of the generated key in bytes. The key will automatically be set up with cx7's meta data, the key position. So a 1024 bytes key will result in 1028 bytes with meta information included.
Alternatively type "g".

This function uses ruby's random number generator. Be advised that depending on your machine this RNG can be flawed. Without further consideration and testing, keys generated with this function CANNOT BE TRUSTED!
This function is only for generating keys FOR TESTING PURPOSE! For production environments consider to generate keys on true hardware RNGs.

3.3.7 # help

See 3.3 Command list

3.3.8 # identify

When an identserver has been set up (using setidentserver), this command identifies the current host on this server. identify AB01 password where "AB01" is the registered identification and "password" the linked password. If ID and password are matching, the server accepts the identification and updates its database to the current IP-address of the requesting machine. To enforce a specific IP-address (different from the currently used address) and a different port than the standard port "7171", you can add two more parameters: identify AB01 password 7070 where "7070" is the port you want to enforce and "" the altering IP-address. The server also stores the timestamp of the last update.
Alternatively type "id".

3.3.9 # keyposition

This command reads the current position of a cx7 OTP key. To read the current position of a cx7 OTP key type: keyposition /path/to/key.cx7 where "/path/to/" is the directory and "key.cx7" the filename of the key. You don't need to specifiy path and position when you have set up a key with the setkey function. In this case you can simply type: keyposition cx7 then returns the current position, like: 0015 where the leading zeros (respectively number of digits) represent the decimal length of the byte-size (1024 for example is 4, 65536 is 5) and the number is the current position of the key, from where messages or files will be en- or decrypted.
Alternatively type "kp".

3.3.10 # localdecrypt

This command decrypts a local file. localdecrypt /path/to/enc_file.cx7 /path/to/file.cx7 where the first parameter is the path and filename of the encrypted file and the second will be the path and name of the decrypted file.
As an optional thrid parameter a path and filename of a key to use for decryption can be specified. If not specified the key will be used which was set up with setkey.
This function regards the cx7 OTP key meta-data. After every successful en- or decryption the key position will be automatically updated.
Alternatively type "ld".

3.3.11 # localencrypt

This command encrypts a local file. localencrypt /path/to/file.cx7 /path/to/enc_file.cx7 where the first parameter is the path and filename of the clear file and the second will be the path and name of the encrypted file.
As an optional thrid parameter a path and filename of the key to use for encryption can be specified. If not specified the key will be used which was set up with setkey.
This function regards the cx7 OTP key meta-data. After every successful en- or decryption the key position will be automatically updated.
Alternatively type "le".

3.3.12 # localswitch

This command switches a local file. The function does the same as localdecrypt and localencrypt but without a specific direction; which is possible for the OTP underlying XOR operand. This function does not show any progress and might be slightly faster. localswitch /path/to/file1.cx7 /path/to/file2.cx7 You can use it for multiple switches with several keys.
After every successful en- or decryption the key position will be automatically updated.

3.3.13 # msg

After establishing a connection to a host or guest and after setting up a key, you can use the "msg" command to send an encrypted message. msg Hello, this is an encrypted message. If the message was transferred successfully, the remote peer answers with an automatic responder, like: [| x OK which indicates the receipt but not necessarily the successful decryption.
After every successful en- or decryption the key position will be automatically updated.
Alternativeley type "m".

3.3.14 # serve

This command starts a serving thread which allows the machine to act as a host. serve 7171 where "7171" is the port and "" the IP-address to bind. Be advised that the IP-address must be reachable from the outside. Also the port must be open. If you can't bind your outside IP-address, because you are behind a router or NAT for example, consider to bind "" to listen to any connection which reaches the port. Consider security risks in this case though. If everything goes well, cx7 will output something like that: # Starting thread for socket server ... OK # Server setup on 7171 # Binding IP and port ... OK # Listening to socket ... OK # Awaiting incomming connection ... The socket will accept exactly one connection. First come, first serve. You can "unserve" to shutdown the server to end a connection. On some OS a shutdown will not free the bound port immediately. In this case you have to wait a little bit to re-start the server.
If a connection is accepted, the server will STDOUT something like this: # Connection established with where "" is the IP-address of the remote peer.
Alternativeley type "s".

3.3.15 # setidentserver

With this command you can set up the ident-server, which cx7 should use for simple DNS resolution. setidentserver 7172 where "" is the domain or IP-address of the DNS server and "7172" the port the server listens to. cx7 will check if the ident-server is reachable and communicating properly. If successful, you get an output like: # Checking server at ... OK Alternatively type "sid".

3.3.16 # setkey

When transferring encrypted messages or files, cx7 needs the respective one-time pad for en- and decryption. With this command you set up which key will be used for the current session. You can change the key on-the-fly if needed, for example when you have one key for messaging and another for files. setkey /path/to/key.cx7 where "/path/to/" is the directory of your key and "key.cx7" its filename. Be advised that the key must be readable as well as writeable for cx7, since it automatically updates its meta-data. When the key is at least readable, cx7 outputs something like: # Key set as /path/to/key.cx7 Alternatively type "sk".

3.3.17 # stat

This command manually performs a DNS lookup on a cx7 ident-server. This function is autmatically called when you connect with an ID string. stat AB01 where "AB01" is the registered ID string you want to resolute. Before you can call that command, you have to set up an ident-server with setidentserver.

3.3.18 # sync

After connecting to a peer and setting up the respective key, you can use this command to get information for synchronising the keys. For example when they went off-sync afer some local en- or decrypting. If you use this command, cx7 will read-out the current key position from your local file and will send it to your peer. Then the peer answers automatically with the position of the remote key. sync An answer can look like: [| x sync 0018 The remote peer sees the sync like this: [| # sync 0015 In this case the keys are off-sync. The host's key (at is at position 15 and the peer's key (at is at position 18. It should be regarded that it is always a best practice to re-adjust a key only in the upwards direction. So in this case the host will update the keyposition to 18. This way you reduce the risk of parts of the key being used more often than once.

3.3.19 # unconnect

This command ends an established connection and closes the thread, which was established with the command connect.
Alternatively type "uc".

3.3.20 # unserve

This command ends an established connection, stops the server and closes the thread, which was made with the command serve.
Alternatively type "us".

3.3.21 # updateposition

When you have set up a key, you can use this command to change its meta-data. updateposition 18 where "18" is the new position which should be stored in the key's meta-data. You also can use an optional second parameter to specify a key: updateposition 18 /path/to/key.cx7 where "/path/to/key.cx7" is the directory and filename of the key you want to be affected. If the procedure is successful, cx7 outputs something like: # Updating key position /path/to/key.cx7 to 18 ... OK Alternatively type "up".

3.4 Working with cx7-server

You can install cx7-server just as cx7-nix. The server version does not have an interactive mode. You can start the server simply with: ruby cx7-server.rb The server starts at default on If you want to change the IP or port, you have to hardcode it in the source-code: sockaddr = Socket.sockaddr_in(7171, '') cx7-server needs the necessary rights to read/write to a Sqlite3 Database in the same directory as the program. The db will be created at start-up if not existing already.
The server program accepts a parameter at start-up to either list or prune the database entries: ruby cx7-server.rb list will output for example something like this: AB01 | mypass++ | | 7171 | 200722 225416 AB02 | 2npass++ | | 7171 | 200723 155453 where the first column is the ID string (= 4 bytes), the second is the password (max. 8 bytes), the third the IPv4 address, the fourth the port (int 2^16), and the last is a timestamp (YYMMDD HHMMSS).
The command: ruby cx7-server.rb prune will delete all entries from the database.
If you want to start cx7-server as a background process (daemon) on n*x systems, you can start it like this: ruby cx7-server.rb >log.txt 2>&1 & which will result in a background run while piping STDOUT and SDTERR to log.txt.

3.5 The cx7 protocol

cx7-nix's communication protocol is based on TCP. It establishes a persitent connection and uses an end-flag for reading/writing and no headers. The peer2peer communication's end-flag is EOFEOF with space of 4 prepending bytes for meta-information. For example: CLEREOFEOF implicates the prepending message is clear-text. ACPT = accept file transfer CLER = clear message FILE = file requeset MSSG = encrypted message NORE = no reply OKOK = everything ok SYNC = synchronisation demand TRNS = file transfer The cx7-server communication is based on a fixed-length header with no end-flag. It always reads 32 bytes from the socket stream and then closes the session. socket.write("TSUC----++++----++++----++++----") is the server's answer to a peer's test connection. The "+" and "-" are redundant bytes to fill the header and can be any character. DBUP = database update ERID = error in ID ERPW = error in password FAIL = a failure appeared IDCR = ident created IDFY = identify, peer's request to login/register OKOK = everything ok STAT = request status information for an registered ID TEST = request test connection TSUC = successful test answer Flags can be combined. For example FAILERPW implicates that there was a failure related to an error in the password.
The "identify" header expects the first 4 bytes to be IDFY, the next 4 bytes to be the ID, the next 8 bytes are a password, the next 4 bytes are the port and the next 4 the IP-address packed in a 8bit char sequence. ++++ ---- ++++---- ++++ ---- ++++ ---- IDFY AB01 password \x1C\x03++ \x7F\x00\x00\x01 ++++ ---- This identifies the ID "AB01" with the password "password" on the port "7171" and IP "".

The simple DNS server is not made for a high security standard. It is not recommended that crucial information should be managed via this server. The server helps to avoid sharing IP-addresses. In case your IP-address is a crucial information, you should definitely not use this simple DNS server. If your IP-address is not curical and/or available anyways, then you can just use cx7-server as good as any other method of transferring your IP-address, since the OTP-en-/decrypting is not affected by this at all.

4. Library

To include the basic cx7 functions to your script environment, you can simply include the file cx7_func.rb include ( "/path/to/cx7_func.rb" ) # or require ( "/path/to/cx7_func.rb" )

4.1 Function list

4.1.1 generatekey(name, size)

This function expects two parameters, the path to store the key at and the size it shall have in bytes. The function generates random bytes and stores them into the given file. It also stores the meta-data at the beginning of the file. The meta-data contains the key position at the beginning of the file. The keyposition starts at the length of the header + 1. The length of the header is the size of the key as decimal digits. 2^13 bit = 1024 bytes = 4 decimal digits headerlength = 4 bytes startposition = headerlength + 1 = 5 cx7 always works byte-wise. So the smallest unit to waste from the key is one byte. # structure of a cx7 OTP file header | random data | 0005»4¶“löa!$öja8oi4»“rŋµæ→ie... → This function returns "true" on success.

This function uses ruby's random number generator. Be advised that depending on your machine this RNG can be flawed. Without further consideration and testing, keys generated with this function CANNOT BE TRUSTED!
This function is only for generating keys FOR TESTING PURPOSE! For production environments consider to generate keys on true hardware RNGs.

4.1.2 fetch_key_position(key)

This function reads out the current key position of a cx7 OTP file. Which means it reads its header. The parameter "key" is the path and filename of the key. The file must be readable.
→ This function returns the key position as a string.

4.1.3 fetch_key_position_from_file(key, file)

This functions fetches the header from a file, which is not the key. It needs the key though, to obtain the size of the header of the file. Key and file must be readable. # key (1024 bytes): 0005»4¶“löa!$öja8oi4»“rŋµæ→ie... # file (64 bytes): 0011æſð80w4oigalyvæł¶gæ³¼ŋ89E... # return is "0011", the header of the file with 4 bytes, although "file"-header # would have been only 2 bytes, due to its shorter length of 64 bytes. → This function returns the header of the file as a string.

4.1.4 msg_switch(str, key)

This function depends on the function fetch_key_position, read_from_key and update_key_position. The function expects a string as the first parameter and the path/name of a key file as the second. The function reads the header of the key file and reads the related bytes, in the length of the string, which was given as the first parameter. Then it switches the bytes of the string against the bytes from the key.
The core function of the OTP is implemented here: e = c mod(2) k # or e = c XOR k # or e = c ^ k where "e" is the encrypted byte, "c" the clear byte and "k" the key byte.
For XOR it is irrelevant in which direction you switch. e = c ^ k c = e ^ k # because bit-wise seen, as an example: 1 = 0 XOR 1 0 = 1 XOR 1 Therefore the first parameter can be a string containing the clear message / binary data or being encrypted.

4.1.5 msg_switch_prg(str, key)

Like msg_switch() but sends status information to STDOUT.

4.1.6 msg_switch_from_position(str, key, position)

Like msg_switch() but enforce a specific position to read the key from.

4.1.7 msg_switch_from_position_prg(str, key, position)

Like msg_switch_from_position() but sends status information to STDOUT.

4.1.8 read_from_key(key, position, length)

This function opens "key" and reads "length" bytes starting from "position".
→ This function returns binary data as string.

4.1.9 update_key_position(key, newposition)

This function updates the key's meta-data to "newposition".
→ This function returns true on success.

5. Issues

cx7 is in alpha status. Which means it is available for testing. Since it is not expected that a broad public will use this software, there will be no beta but a stable version after a testing phase with a group of developers.

5.1 Speed

One important issue is speed. At this time, cx7 uses the serial way and switches clear against key byte-wise. This is maybe the most accurate way, and more than sufficient for "real-time" text communication, but it can be quite slow for large files. Of course a solution could be to subsume bytes in a chain which can be switched at once. Especially for local en- and decrypting of binary data. A > 65 > 01000001 (key) 10101101 > 173 > ¡ (XOR) ________ 11101100 > 236 > ý One single character switch (in an 8bit char-set) will understress the computing unit but enforce a huge load of single operations on a large byte-load. Especially for crypting local files, a clever balance between chunks and memory capacity could be found, and a switch of e.g. 1 billon bytes could be done in a few operations. For example, assuming a limited memory of 2GB (incl. a running OS etc.), load 500MB from the file to memory, switch it at whole, store the chunk, free the memory, load the second 500MB, switch and append to the first chunk, free the memory.
Since it cannot be known on what device cx7 will be used, the lowest common denominator is a chunk of one byte. But in this case, the clear string or data must be loaded into the memory as a whole. Freeing the memory after one byte would be kind of a waste. So the paradox situation appears, that with a smaller chunk the maximum file size, which can be switched in the memory, lowers.
As a convenient next development step, a configuration file for cx7 will be implemented, in order to be able to adjust serial/parallel processing to the device.

5.2 Development


6. Disclaimer


7. License and copyright

License is to be announced.

Copyright © 2020, openpot Media and contributors. All Rights Reserved.