• NEWSCARD * Publish and fetch permanent named records via NetworkNews

    From Byrl Raze Buckbriar@news0@octade.net to alt.2600,alt.2600.hackers,alt.cyberpunk on Saturday, February 08, 2025 23:45:23
    From Newsgroup: alt.2600

    * NEWSCARD * Publish and fetch permanent named records via Network News Newscard creates a decentralized, named record pastebin.
    [git repo] https://codeberg.org/OCTADE/newscard
    * NEWSCARD VERSION 0.0.1.D *
    * TABLE OF CONTENTS *
    1. NEWSCARD INFO
    2. NEWSCARD TIPS
    3. NEWSCARD HELP AND USAGE
    4. NEWSCARD TEST KEYS
    5. NEWSCARD SOURCE CODE (xz compressed base64)
    6. NEWSCARD SOURCE CODE (plain text)
    * 1. NEWSCARD INFO *
    Author: Byrl Raze Buckbriar (https://octade.net) (news://alt.rhubarb)
    Newscard encrypts a local text file and publishes it via NNTP.
    The name assigned to the card is the address and passphrase.
    Anyone who knows the name or passphrase can retrieve the card.
    A very strong passphrase will keep the card content secret.
    Newscard uses nine layers of encryption and chain hashes that are
    derived from multiple hash streams and large, random salt.
    Use 'tuff' mode for very strong encryption passphrases.
    Try 'card help' for usage instructions.

    User License: Use solely at your own risk. By using this software the user
    agrees that the author has zero liability for any loss, harm, or damage.
    This software is 'doodleware' and not fit for any commercial purpose.
    The user has no rights whatsoever related to the use of this software
    or any thing arising from such use. The user must agree to this license
    to use the software. There is no support, guarantee, or warranty.
    * 2. NEWSCARD TIPS *
    Ways you can use newscard include:
    1. Publishing and retrieving scripts, sources, and articles with a phrase.
    2. Publishing a personal profile others can retrieve with a keyword.
    3. Exchanging encrypted messages (using strong keys or 'tuff' mode).
    4. Including newscard keys in signatures and articles.
    Once published, a newscard remains a permanent record.
    * 3. NEWSCARD HELP AND USAGE *
    WARNING: Use 'tuff' mode for high security against decryption.
    Tuff mode generates cards with high-security, strong passphrases.
    Enc mode forces a minimum custom key size and still may be insecure.
    Enc mode should be used if you want the public to decrypt the card.
    Tuff mode should be used to prevent the public from reading the card.
    show this help message : card help
    show some tips and use cases : card tips
    show author and license info : card info
    generate highly secure news card : card tuff [path|file]
    generate news card via password : card enc [passphrase] [path|file]
    fetch card from network : card get [passphrase]
    publish card to network : card put [passphrase]
    read text of local news card : card show [passphrase]
    list stored cards by folder : card list [draft|queue|inbox|sent]
    Author: Byrl Raze Buckbriar (https://octade.net) (news://alt.rhubarb)
    * 4. NEWSCARD TEST KEYS *
    Users may test the Newscard application by fetching these cards by key:
    sample1 | sample2 | sample3 | octade | newscard0
    To fetch a card use the command: card get [key].
    Examples below can be tried for cards that are already on the network:
    $~: card get newscard
    $~: card get 8
    $~: card get sample1
    * 5. NEWSCARD SOURCE CODE (xz compressed base64) *
    (commands to decompress: cat | base64 -d | unxz) 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
    6. NEWSCARD SOURCE CODE (plain text)
    #!/usr/bin/env bash
    # NEWSCARD VERSION 0.0.1.D - Use subject to license included below.
    # ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄ ▄▄▄
    # █ █ █ █ █ █ █ █ █▄▄ Byrl Raze Buckbriar # █▄█ █▄▄ █ █▀█ █▄▀ █▄▄ https://octade.net
    # warning: this is a 'D' version for DOODLE. It is likely to have eventual regression in the key generation and card schema format--that breaks backward compatibility. The purpose of the doodle version is to test and generate feedback for improvement. Expect a final regression rendering existing newscards inaccessible to the final version.
    # zombies: some unused code and paths here for future features. sections labeled 'zombie'.
    # INSTALL: Copy to $PATH and make executable (chmod u+x).
    # BEGIN.NEWSCARD
    DIE() { echo "$@" && exit 0 ; }
    ERR() { echo "CARD: ERR: $@" 1>&2 && exit 1 ; }
    check_deps() {
    deps="tor bash b2sum base64 echo fold grep head less nc openssl shuf tail tee tr whoami xxd xz"
    for prog in $deps
    do
    [[ $(which $prog) == '' ]] && ERR "[DEP1] Program ($prog) not found." && exit 1 done
    }
    check_deps
    HELPTEXT="* NEWSCARD HELP AND USAGE *
    WARNING: Use 'tuff' mode for high security against decryption.
    Tuff mode generates cards with high-security, strong passphrases.
    Enc mode forces a minimum custom key size and still may be insecure.
    Enc mode should be used if you want the public to decrypt the card.
    Tuff mode should be used to prevent the public from reading the card.
    show this help message : card help
    show some tips and use cases : card tips
    show author and license info : card info
    generate highly secure news card : card tuff [path|file]
    generate news card via password : card enc [passphrase] [path|file]
    fetch card from network : card get [passphrase]
    publish card to network : card put [passphrase]
    read text of local news card : card show [passphrase]
    list stored cards by folder : card list [draft|queue|inbox|sent]
    Author: Byrl Raze Buckbriar (https://octade.net) (news://alt.rhubarb)" TIPSTEXT="* NEWSCARD TIPS *
    Ways you can use newscard include:
    1. Publishing and retrieving scripts, sources, and articles with a phrase.
    2. Publishing a personal profile others can retrieve with a keyword.
    3. Exchanging encrypted messages (using strong keys or 'tuff' mode).
    Once published, a newscard remains a permanent record."
    INFOTEXT="* NEWSCARD INFO *
    Author: Byrl Raze Buckbriar (https://octade.net) (news://alt.rhubarb)
    Newscard encrypts a local text file and publishes it via NNTP.
    The name assigned to the card is the address and passphrase.
    Anyone who knows the name or passphrase can retrieve the card.
    A very strong passphrase will keep the card content secret.
    Newscard uses nine layers of encryption and chain hashes that are
    derived from multiple hash streams and large, random salt.
    Use 'tuff' mode for very strong encryption passphrases.
    Try 'card help' for usage instructions.

    User License: Use solely at your own risk. By using this software the user
    agrees that the author has zero liability for any loss, harm, or damage.
    This software is 'doodleware' and not fit for any commercial purpose.
    The user has no rights whatsoever related to the use of this software
    or any thing arising from such use. The user must agree to this license
    to use the software. There is no support, guarantee, or warranty." show_help() { DIE "$HELPTEXT" ; } # FIN.
    show_info() { DIE "$INFOTEXT" ; } # FIN.
    show_tips() { DIE "$TIPSTEXT" ; } # FIN.
    # non-global variables: session
    # global variables : TODO : zombies for future features
    declare -g arg1 arg2 arg3 arg4 arglen
    declare -g pp1 pp2 passphrase hash msgid subid keyid
    declare -g filename mode flag article msg newmsg mray stripcard
    declare -g nzconf nzlogs nzmani nzpath nzprox nzqqqq zdraft narxiv nzread nzsent nzserv nzuser nzwarn
    declare -g iter=() salt=() hashphrase=()
    declare -g x inum bigsalt encsalt
    declare -g newserv port logfile logtrunc
    declare -g config_data proxy_data warn_data
    declare -g prefix version
    declare -g cip pak
    declare -g listfolder
    prefix="NEWSCARD"
    version="0.0.1.D"
    arglen=${#@}
    # push command arguments to global variables
    arg1="$1" ; arg2="$2" ; arg3="$3" ; arg4="$4"
    # exit if too many arguments found
    [[ "$arg4" != '' ]] && ERR "[ARG4] Too many arguments. Try mode 'help'."
    [[ $arglen -gt 3 ]] && ERR "[ARG3] Too many arguments. Try mode 'help'." # edge case zombie code
    mode=$arg1 # arg1 must always be a valid mode selector word
    set_mode() {
    # zombie : future refactoring, so some of these modes are not enabled
    # valid modes : enc, dec, get, put, show, list, delete, put_unencrypted, save, export, help
    # read the first command argument
    [[ "$mode" == "help" ]] && show_help
    [[ "$mode" == "info" ]] && show_info
    [[ "$mode" == "tips" ]] && show_tips
    [[ "$mode" == "enc" ]] && flag='true'
    [[ "$mode" == "tuff" ]] && flag='true'
    [[ "$mode" == "dec" ]] && flag='true'
    [[ "$mode" == "get" ]] && flag='true'
    [[ "$mode" == "put" ]] && flag='true'
    [[ "$mode" == "show" ]] && flag='true'
    [[ "$mode" == "list" ]] && flag='true'
    #[[ "$mode" == "delete" ]] && flag='true' # zombie
    #[[ "$mode" == "put_unencrypted" ]] && flag='true' # zombie
    #[[ "$mode" == "save" ]] && flag='true' # zombie
    #[[ "$mode" == "export" ]] && flag='true' # zombie
    #[[ "$mode" == "pix" ]] && flag='true' # zombie
    #[[ "$mode" == "send" ]] && flag='true' # zombie
    # check for a valid mode before continuing
    [[ "$flag" != 'true' ]] && ERR "[FLAG1] Invalid mode selection ($mode) or wrong # of arguments. Try mode 'help'."
    # check for correct number of commandline arguments for mode
    [[ "$mode" == "enc" ]] && [[ $arglen -ne 3 ]] && flag='false'
    [[ "$mode" == "tuff" ]] && [[ $arglen -ne 2 ]] && flag='false'
    [[ "$mode" == "dec" ]] && [[ $arglen -ne 2 ]] && flag='false'
    [[ "$mode" == "get" ]] && [[ $arglen -ne 2 ]] && flag='false'
    [[ "$mode" == "put" ]] && [[ $arglen -ne 2 ]] && flag='false'
    [[ "$mode" == "show" ]] && [[ $arglen -ne 2 ]] && flag='false'
    [[ "$mode" == "list" ]] && [[ $arglen -ne 2 ]] && flag='false'
    # zombie : future refactoring
    #[[ "$mode" == "delete" ]] && [[ $arglen -ne 2 ]] && flag='false' # zombie
    #[[ "$mode" == "put_unencrypted" ]] && [[ $arglen -ne 3 ]] && flag='false' # zombie
    #[[ "$mode" == "save" ]] && [[ $arglen -ne 3 ]] && flag='false' # zombie
    #[[ "$mode" == "export" ]] && [[ $arglen -ne 2 ]] && flag='false' # zombie
    #[[ "$mode" == "pix" ]] && [[ $arglen -ne 2 ]] && flag='false' # zombie
    [[ "$flag" == 'false' ]] && ERR "[FLAG2] Wrong # of arguments for mode ($mode). Try mode 'help'."
    # copy command argument parameters to variables based upon mode
    [[ "$mode" == "enc" ]] && passphrase=$arg2 && filename=$arg3 # && echo $arg3 && echo $filename && exit
    [[ "$mode" == "tuff" ]] && passphrase='' && filename=$arg2 # && echo $arg3 && echo $filename && exit
    [[ "$mode" == "dec" ]] && passphrase=$arg2
    [[ "$mode" == "get" ]] && passphrase=$arg2
    [[ "$mode" == "put" ]] && passphrase=$arg2 && filename=$arg3
    [[ "$mode" == "show" ]] && passphrase=$arg2
    # zombie : future refactoring
    #[[ "$mode" == "delete" ]] && passphrase=$arg2 # zombie
    #[[ "$mode" == "put_unencrypted" ]] && passphrase=$arg2 && filename=$arg3 # zombie
    #[[ "$mode" == "save" ]] && passphrase=$arg2 && filename=$arg3 # zombie
    #[[ "$mode" == "export" ]] && passphrase=$arg2 && filename=$arg3 # zombie
    #[[ "$mode" == "pix" ]] && exit # zombie
    }
    set_mode # TODO need to position so it can be removed from functions below check_filename() { # TODO refactor or delete since filename is passed now
    # read file path to variable if mode includes file
    [[ ! -f $filename ]] && ERR "[FILE1] File not found." && exit 1
    }
    check_filesize() { # TODO refactor or delete since filename is passed now
    # read file path to variable if mode includes file
    [[ ! -f $filename ]] && ERR "[FILE2] File not found." && exit 1
    filesize=$(cat "$filename" | wc -c | tr -dc 0-9)
    [[ $filesize -gt 65536 ]] && ERR "[SIZE1] File size ($filesize) exceeds maximum (65536 bytes)." && exit 1
    }
    check_newscard_exists() { # TODO : zombie
    # read file path to variable if mode includes file
    [[ -f "$zdraft/$passphrase" ]] && ERR "[CNE1] A draft card already exists for this passphrase." && exit 1
    [[ -f "$zinbox/$passphrase" ]] && ERR "[CNE2] A inbox card already exists for this passphrase." && exit 1
    [[ -f "$nzqqqq/$passphrase" ]] && ERR "[CNE3] A queue card already exists for this passphrase." && exit 1
    [[ -f "$nzsent/$passphrase" ]] && ERR "[CNE4] A sent card already exists for this passphrase." && exit 1
    [[ -f "$narxiv/$passphrase" ]] && ERR "[CNE5] An archive card already exists for this passphrase." && exit 1
    [[ -f "$nzhist/$passphrase" ]] && ERR "[CNE6] An history card already exists for this passphrase." && exit 1
    # TODO : add method to check history and confirmed publish lists
    }
    # network connections - for now only torconn is used to force privacy.
    # zombie : future refactoring
    sslconn() {
    logfile=$nzconn
    openssl s_client -quiet -connect $newserv:$port >> $logfile 2>&1 # TODO : s_client info mucks up parsing
    logtrunc=$(cat "$logfile" | tail -n 4096)
    echo "$logtrunc" > "$logfile"
    }
    # zombie : future refactoring
    telconn() {
    nc $newserv $port >> $logfile 2>&1 # s_client info mucks up parsing logtrunc=$(cat "$logfile" | tail -n 4096)
    echo "$logtrunc" > "$logfile"
    } # TODO zombie code
    torconn() {
    # TODO : add authentication credential parsing
    logfile=$nzconn
    newserv=$(cat $nzserv | grep -v '#' | grep ':server:' | tail -n 1 | cut -d ':' -f3- | tr -d ' ')
    port=$(cat $nzserv | grep -v '#' | grep ':port:' | tail -n 1 | cut -d ':' -f3- | tr -d ' ')
    timeout 20 torsocks openssl s_client -quiet -verify_quiet -connect $newserv:$port | tee -a $logfile # 2>/dev/null # 2>&1 # TODO : s_client info mucks up parsing
    logtrunc=$(cat "$logfile" | tail -n 4096)
    echo "$logtrunc" > "$logfile"
    }
    # Check for config directory. Create directory and missing config files. set_conf_data() {
    config_data=":server: paganini.bofh.team
    :port: 563
    # username:
    # password:
    "
    echo "$config_data" > $nzserv
    }
    set_prox_data() {
    # TODO : zombie
    proxy_data="# host: 127.0.0.1
    # port: 9050
    "
    echo "$proxy_data" > $nzprox
    }
    set_warn_data() {
    warn_data="
    --------------------------------
    WARNING! README! INSTRUCTIONS!
    --------------------------------
    This folder contains the configuration files for the newscard application.
    If you do not know exactly what you are doing then you should not change
    any of these files. If you change the files in this folder you may break
    the application and cause errors or data loss. The file names are cipher
    keys and thus changing the file names will make decryption impossible.
    The program author WILL NOT respond to requests regarding broken configuration. The only way to fix a broken configuration is to re-install the program. This will likely result in the loss of all data. Back up your keycard passphrases and files regularly to prevent losing access to your files.
    ----------------------------------
    ABOUT THESE CONFIGURATION FOLDERS
    ----------------------------------
    config : configuration files (see below)
    draft : unpublished newscards
    inbox : downloaded newscards
    queue : newscards waiting to be sent
    sent : published newscards
    archive : archived newscards (not shown via inbox view)
    ---------------------------------
    ABOUT THESE CONFIGURATION FILES
    ---------------------------------
    newscard.server : NNTP server settings
    newscard.proxy : proxy settings
    newscard.manifest : component configuration data
    newscard.log : activity and error logs
    "
    echo "$warn_data" > $nzwarn
    echo "$warn_data" > $nzread
    }
    set_nzpaths() {
    # Set paths for functions to write and read files
    nzuser=$(whoami)
    nzpath="/home/$nzuser/.newscard"
    # Message directories
    nzsent="$nzpath/sent"
    zinbox="$nzpath/inbox"
    nzqqqq="$nzpath/queue"
    zdraft="$nzpath/draft"
    narxiv="$nzpath/archive"
    nzhist="$nzpath/history"
    # Configuration files
    nzconf="$nzpath/config"
    nzserv="$nzconf/newscard.server"
    nzprox="$nzconf/newscard.proxy"
    nzmani="$nzconf/newscard.manifest"
    nzlogs="$nzconf/newscard.log"
    nzconn="$nzconf/connect.log"
    # Warning files
    nzread="$nzpath/WARNING.README"
    nzwarn="$nzconf/WARNING.README"
    }
    # zombie : future refactoring
    create_config() {
    [[ ! -d $nzpath ]] && mkdir $nzpath && chmod 0700 $nzpath
    [[ ! -d $nzconf ]] && mkdir $nzconf && chmod 0700 $nzconf
    [[ ! -f $nzserv ]] && set_conf_data && chmod 0600 $nzserv
    [[ ! -f $nzmani ]] && touch $nzmani && chmod 0600 $nzmani
    [[ ! -f $nzlogs ]] && touch $nzlogs && chmod 0600 $nzlogs
    [[ ! -f $nzconn ]] && touch $nzconn && chmod 0600 $nzconn
    [[ ! -f $nzprox ]] && set_prox_data && chmod 0600 $nzprox
    [[ ! -d $nzsent ]] && mkdir $nzsent && chmod 0700 $nzsent
    [[ ! -d $zinbox ]] && mkdir $zinbox && chmod 0700 $zinbox
    [[ ! -d $zdraft ]] && mkdir $zdraft && chmod 0700 $zdraft
    [[ ! -d $nzqqqq ]] && mkdir $nzqqqq && chmod 0700 $nzqqqq
    [[ ! -d $narxiv ]] && mkdir $narxiv && chmod 0700 $narxiv
    [[ ! -d $nzhist ]] && mkdir $nzhist && chmod 0700 $nzhist
    [[ ! -f $nzwarn ]] && set_warn_data && chmod 0600 $nzwarn
    }
    set_list_paths() {
    # Set paths for functions to write and read files
    nzuser=$(whoami)
    nzpath="/home/$nzuser/.newscard"
    # Message directories (zombie)
    nzsent="$nzpath/sent"
    zinbox="$nzpath/inbox"
    nzqqqq="$nzpath/queue"
    zdraft="$nzpath/draft"
    narxiv="$nzpath/archive"
    nzhist="$nzpath/history"
    }
    check_config_folder() {
    nzpath="/home/code/.newscard"
    [[ ! -d $nzpath ]] && echo "Newscard config not found at '$nzpath'. Creating config now." && set_nzpaths && create_config
    }
    # Create configuration if not present.
    check_config_folder
    check_passphrase() {
    passphrase=$arg2 # arg vars work but passphrase global doesn't work pp1="$passphrase"
    pp2=$(echo "$pp1" | tr -dc A-Za-z0-9.@#+=_\\-~)
    [[ "$pp1" != "$pp2" ]] && ERR "[PP12] Invalid passphrase. Valid characters are 'A-Za-z0-9.@#+=_-~'."
    [[ ${#pp1} -gt 255 ]] && ERR "[PP255] Invalid passphrase. Passphrase must be less than 255 chars."
    }
    # Extra random generation for salts.
    ts() {
    echo "$(date +%N)$(date +%N)$(date +%N)$(date +%N)" | fold -w 1 | shuf | tr -dc 0-9
    }
    timestamp() {
    echo $(ts)$(ts)$(ts)$(ts)$(ts)$(ts)$(ts)$(ts) | fold -w 1 | shuf | tr -dc 0-9
    }
    minesalt() {
    bigsalt=$(timestamp)
    bigsalt=$(head -c 2048 /dev/urandom | xxd -p | fold -w 1 | shuf -n 1023 | sivhex | trisum)$bigsalt
    bigsalt=$(echo -n "$bigsalt" | fold -w 1 | shuf | sivhex | trisum)
    }
    # encoding and conversion functions (some zombie code for future refactoring) enchex() { xxd -p -r ; }
    dechex() { xxd -p -c 0 ; }
    encb64() { base64 -w 0 ; }
    decb64() { base64 -d ; }
    sivhex() { tr -dc a-f0-9 ; } # clean hashes of non-hex chars
    sivb62() { tr -dc A-Za-z0-9 ; }
    smoosh() { xz -q -F raw ; }
    unmash() { xz -d -q -F raw ; }
    bzoosh() { bzip2 -q ; } # better small file compression on single core
    b64toz() { tr '=' '@' | tr '+' '#' | tr '/' '$' ; }
    ztob64() { tr '@' '=' | tr '#' '+' | tr '$' '/' ; }
    fmtmsg() { fold -w 13 | tr '\n' ' ' | fold -w 71 ; }
    # chain hash stream without for loops
    beesum() { b2sum ; }
    onesum() { beesum | sivhex ; }
    trisum() { onesum | onesum | onesum ; }
    ninsum() { trisum | trisum | trisum ; }
    bigsum() { ninsum | ninsum | ninsum ; } # chain hash 81x to resist search
    # global hash stream
    nexsum() { hash=$(echo -n $hash | trisum) ; }
    # generate NNTP Message-ID
    gen_msgid() { # mast pass global to this function
    # generate base62 message-id to identify either message or file
    # communicants can use a custom secret keyid here for out-of band security keyid="$msgid" # newscard.default.keyid (change for secret networks) msgid="$keyid$msgid$keyid" # never change this!
    # any change in this method will regress fetch of all prior newscards
    # this must be done before forking the hash stream with big salt
    msgid=$(echo -n "$msgid$msgid$msgid" | bigsum) # clone expand msgid msgid=$(echo -n $msgid | xxd -p -r | base64 -w 0 | sivb62 | tail -c 43) subid=$msgid # for the NNTP subject header
    msgid="<$msgid@news.card>"
    }
    # read the user-supplied passphrase and save for later naming and decryption get_pass_hash() {
    hash="$passphrase"
    }
    # superencryption routine using 9 keys and 9 salts.
    generate_credentials() {
    # declare some sets
    iter=() ; salt=() ; hashphrase=()
    # get encryption params for bigsalt before we salt and fork the hash stream nexsum ; salt[0]=${hash:0:16}
    nexsum ; hashphrase[0]=$hash
    nexsum
    inum=${hash:0:3}
    inum=$(printf '%d' 0x$inum)
    inum=$((10#$inum))
    iter[0]=$((16384+inum))
    # SALT MODES : if mode generate mix random big salt only in encrypt mode
    [[ "$mode" == 'enc' ]] && bigsalt=$(head -c 1023 /dev/urandom | xxd -p | fold -w 1 | shuf -n 1023 | sivhex | trisum)
    [[ "$mode" == 'tuff' ]] && bigsalt=$(head -c 64 /dev/urandom | xxd -p -c 0 | tr -dc a-f0-9) # careful not to advance keygen
    [[ "$mode" == 'dec' ]] && dec_salt
    [[ "$mode" == 'show' ]] && dec_salt
    # combine random salt with passphrase
    hash="$hash$bigsalt"
    # encrypt the salt for the output message
    # derive pseudorandom cipher iterations
    for x in {1..9}
    do
    nexsum
    inum=${hash:0:3}
    inum=$(printf '%d' 0x$inum)
    inum=$((10#$inum))
    iter[$x]=$((16384+inum))
    done
    # derive stream of salts from passphrase
    for x in {1..9}
    do
    nexsum
    salt[$x]=${hash:0:16}
    done # for x
    # generate stream of hashphrases
    for x in {1..9}
    do
    nexsum
    hashphrase[$x]=$hash
    done # for x
    } # generate_credentials
    # encrypt or decrypt the input stream
    pak='-e'
    cip='-chacha20'
    # superencipher the message with nine each of keys, salts, iterations.
    e() { openssl enc $pak $cip -iter "${iter[$1]}" -S "${salt[$1]}" -pass pass:"${hashphrase[$1]}" ; }
    # tunnel 9 encryption streams
    e9() { e 1 | e 2 | e 3 | e 4 | e 5 | e 6 | e 7 | e 8 | e 9 ; }
    # encrypt the big salt
    enc_salt() {
    encsalt=$(echo -n $bigsalt | xxd -p -r | e 0 | base64 -w 0 | tr '=' '@' | tr '+' '#' | tr '/' '$')
    }
    # snarf encrypted salt from the message
    get_salt() {
    bigsalt="$(echo "$msg" | grep '*' | tr -d ' ' | tr -d '\n' | tr '*' '\n' | grep '@' | head -n 1 | tr -d '\n')"
    }
    # decrypt the salt needed for hash key stream generation
    dec_salt() {
    bigsalt="$(echo -n "$bigsalt" | base64 -d | e 0 | xxd -p -c 0 | tr -dc a-f0-9)" }
    encrypt_b64() {
    # TODO : check filename exists
    #check_filename
    msg=$(cat "$filename" | smoosh | e9 | base64 -w 0)
    }
    convert_b64_z64() {
    msg=$(echo -n "$msg" | tr '=' '@' | tr '+' '#' | tr '/' '$')
    }
    convert_b64z_b64() {
    msg=$(echo -n "$msg" | tr '@' '=' | tr '#' '+' | tr '$' '/')
    }
    convert_salt_b64z_b64() {
    bigsalt=$(echo -n "$bigsalt" | tr '@' '=' | tr '#' '+' | tr '$' '/')
    }
    # TODO perhaps write to file or check for file to cat before decrypt decrypt_b64() {
    newmsg=$(echo -n "$newmsg" | base64 -d | e9 | unmash)
    } # notice passing of $newmsg instead of $msg
    assemble_payload() {
    msg=$(echo -n "* $prefix * $version * $encsalt * $msg@" | fold -w 13 | tr '\n' ' ' | fold -w 71)
    } # elegant but this version will resist extensions later
    # TODO : zombies for future refactoring.
    # check_existing_msgid() { echo TODO ; } # zombie
    # confirm_article_post() { echo TODO ; } # zombie
    # copy_draft_to_queue() { echo TODO ; } # zombie
    # remove_posted_queue() { echo TODO ; } # zombie
    # get_remote_card_body() { echo TODO ; } # zombie
    # copy_remote_to_inbox() { echo TODO ; } # zombie
    # delete_by_passphrase() { echo TODO ; } # zombie
    # export_by_passphrase() { echo TODO ; } # zombie
    # delete_sent_article() { echo TODO ; } # zombie
    compose_nntp_article() { # FIN.
    # construct NNTP article for publishing
    article="MODE READER
    POST
    MESSAGE-ID: $msgid
    NEWSGROUPS: alt.cypher
    FROM: News Card $msgid
    SUBJECT: @NEWSCARD@$subid
    $msg
    .
    quit"
    }
    save_composed_article() { # FIN.
    # split into three functions or three passed flags for make, put, fetch
    echo "$article" > "$nzqqqq/$passphrase" # drop in the queue
    }
    save_composed_draft() { # FIN.
    echo "$msg" > "$zdraft/$passphrase" # save just the newscard formatted message locally
    }
    sanitize_message() { # FIN. # TODO disable variable expansion
    declare -g msg
    msg=$(echo -n "$msg" | tr -dc 'A-Za-z0-9#$+.*@ \n') # must cat to prevent variable expansion of $ attack (convert base?)
    }
    parse_article() { # FIN. # need just the body of the article, not the NNTP headers
    declare -g mray
    declare -g msg
    set_nzpaths
    check_passphrase
    # check drafts, outbox, queue, inbox, sent, archive for newscard file
    [[ -f "$zdraft/$passphrase" ]] && draft_flag=true || draft_flag=false
    [[ -f "$zinbox/$passphrase" ]] && inbox_flag=true || inbox_flag=false
    # warn and exit if file not found
    [[ $draft_flag == 'false' ]] && [[ $inbox_flag == 'false' ]] && ERR "[FP1] No file exists for passphrase: $passphrase" && exit 1
    # use flags to choose the available file to parse
    [[ $inbox_flag == 'true' ]] && msg=$(cat "$zinbox/$passphrase") || msg=$(cat "$zdraft/$passphrase")
    # TODO : need to properly manage draft location based on mode and set 0600 perms
    # clean invalid chars and carriage returns
    msg=$(echo -n "$msg" | tr -d '\r')
    mray=()
    mray=($(echo -n "$msg" | tr -d ' ' | tr -d '\n' | tr '*' ' ')) encsalt=${mray[2]} # pass check done
    newmsg=${mray[3]} # get new message to convert to regular base64
    }
    decode_z64_message() { # FIN.
    tr -d ' ' | tr -d '\n' | tr '@' '=' | tr '#' '+' | tr '$' '/'
    }
    strip_salt_tail_z64() { # FIN.
    ats=$(echo -n "$bigsalt" | tr -dc '@')
    atlen=${#ats}
    atlen=$((atlen-1))
    ats=$(echo -n "$ats" | head -c $atlen)
    bigsalt="$(echo -n "$bigsalt" | tr -d '@')"
    bigsalt="$bigsalt$ats"
    }
    strip_newmsg_tail_z64() { # FIN.
    ats=$(echo -n "$newmsg" | tr -dc '@')
    atlen=${#ats}
    atlen=$((atlen-1))
    ats=$(echo -n "$ats" | head -c $atlen)
    newmsg="$(echo -n "$newmsg" | tr -d '@')"
    newmsg="$newmsg$ats"
    } # get that pesky extra '@' that probably should have been a '*' (TODO)
    # TODO check to ensure proper article format and newscard payload format
    # use when publishing and fetching
    # check_composed_article() {}
    # check_received_article() {}
    gen_tuff_params() {
    # mix timestamps and shuffles with PRNG data then whiten with hashing. passphrase=$(echo "$(date +%N)$(date +%N)$(date +%N)$(date +%N)" | fold -w 1 | shuf | sivhex | onesum)$passphrase
    for tuff_rounds in {1..9}
    do
    passphrase=$(echo "$(date +%N)$(date +%N)$(date +%N)$(date +%N)" | onesum)$passphrase
    passphrase=$(echo "$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM$RANDOM" | onesum)$passphrase
    passphrase=$(head -c 32 /dev/urandom | xxd -p -c 0)$passphrase passphrase="$(echo -n $passphrase | fold -w 1 | shuf | sivhex | onesum)$passphrase"
    passphrase="$(echo -n $passphrase | fold -w 1 | shuf | sivhex | tail -c 1024)" done
    passphrase=$(echo -n "$passphrase" | onesum | xxd -p -r | base64 | sivb62) passphrase=$(echo -n "$passphrase" | fold -w 1 | shuf | sivb62 | head -c 43)
    # shift the argument values for tuff make card function
    arg3=$arg2
    arg2=$passphrase
    }
    makemsg() { # FIN.
    [[ "$mode" == "tuff" ]] && gen_tuff_params
    passphrase=$arg2
    msgid=$arg2
    hash=$arg2
    filename=$arg3
    check_filename # check filename exists
    check_filesize # enforce maximum size limit
    set_nzpaths
    check_newscard_exists
    check_passphrase
    gen_msgid
    get_pass_hash
    generate_credentials
    enc_salt
    encrypt_b64
    convert_b64_z64
    assemble_payload # generate message for var 'msg'
    save_composed_draft
    DIE "Created newscard : $passphrase : article in draft folder. Use 'put' to publish."
    }
    decrypt_msg() { # FIN.
    passphrase=$arg2
    msgid=$arg2
    hash=$arg2
    filename=$arg3
    declare -g newmsg
    declare -g mray
    declare -g bigsalt
    declare -g encsalt
    set_nzpaths
    check_passphrase
    gen_msgid
    get_pass_hash
    parse_article # get message segments
    get_salt
    convert_salt_b64z_b64
    generate_credentials
    strip_newmsg_tail_z64
    newmsg=$(echo -n "$newmsg" | decode_z64_message)
    decrypt_b64
    }
    show_newscard() { # FIN.
    decrypt_msg
    (echo "NEWSCARD : $passphrase" ; echo ; echo "$newmsg") | less -R
    DIE
    }
    # zombie: unfinished function
    send_queue() {
    # iterate through queue files and push to nntp server
    set_nzpaths
    check_passphrase
    gen_msgid
    echo "$msgid"
    exit 0
    } # TODO
    # publish a single queued draft instead of whole queue
    # move from queue to sent folder
    publish() {
    declare -g msg
    passphrase=$arg2
    msgid=$arg2
    set_nzpaths
    check_passphrase
    # check for file in drafts
    [[ ! -f "$zdraft/$passphrase" ]] && ERR "[D1] No draft found for passphrase ($passphrase)." && exit 1
    msg=$(cat "$zdraft/$passphrase")
    gen_msgid # TODO only generate draft on enc then nntp message later, or both at once?
    compose_nntp_article
    echo "$article" > "$nzqqqq/$passphrase"
    #session=$(cat "$nzqqqq/$passphrase" | torconn)
    cat "$nzqqqq/$passphrase" | torconn
    exit # zombie below
    # TODO : check for received response or error in session variable
    # then check again for hdr <message-id>
    # check for (stat) 430 No such article
    # clean this up to get a good check via stat, and retry at least 3 times before giving up
    # TODO : if msgid is found, fetch to inbox, compare to draft, then move the draft to archive and the queued to sent
    # TODO : list each successful publish in history publish file.
    exists=$(echo "ARTICLE $msgid" | torconn)
    # TODO : several success checks then move from queue to sent
    echo "$exists" | grep -i "430 No such article" # TODO : convert to trap
    echo "$exists" | grep "$msgid"
    exit 0
    } # TODO: finish the checks and confirmation and file move to sent
    fetch() {
    declare -g msg
    passphrase=$arg2
    msgid=$arg2
    set_nzpaths
    check_passphrase
    # check for file in drafts and inbox and exit if exists
    [[ -f "$zdraft/$passphrase" ]] && ERR "[F1] Card for ($passphrase) already exists in drafts." && exit 1
    [[ -f "$zinbox/$passphrase" ]] && ERR "[F2] Card for ($passphrase) already exists in inbox." && exit 1
    [[ -f "$nzqqqq/$passphrase" ]] && ERR "[F3] Card for ($passphrase) already exists in queue." && exit 1
    gen_msgid
    # NNTP commands to status check for existence of message-id.
    msg="mode reader
    stat $msgid
    quit
    quit"
    nntpout=$(echo -n "$msg" | torconn)
    check200=$(echo -n "$nntpout" | head -c 3)
    [[ $check200 != 200 ]] && ERR "[F5] Invalid server response. Check network." && exit 1
    check223=$(echo -n "$nntpout" | grep '223' | grep -i 'stat' | grep "$msgid" | head -c 3)
    [[ $check223 != 223 ]] && ERR "[F6] Article not found for ($passphrase)." && exit 1
    # If this point is reached server has verified existence of article.
    # NNTP commands to request article body.
    msg="mode reader
    body $msgid
    quit
    quit"
    nntpout=$(echo -n "$msg" | torconn)
    check200=$(echo -n "$nntpout" | head -c 3)
    [[ $check200 != 200 ]] && ERR "[F7] Invalid server response. Check network." && exit 1
    artcheck=$(echo -n "$nntpout" | grep -c NEWSCARD | tr -dc 0-9)
    [[ ! $artcheck -gt 0 ]] && ERR "[F8] No valid card found for ($passphrase)." && exit 1
    # NewsCard body response found; strip nntp server responses.
    stripcard=''
    body222='false' # using string instead of boolean int
    bodydot='false'
    # Strip all non-card lines from article body.
    while IFS= read -r line # Preserve leading and trailing spaces.
    do
    [[ "${line:0:1}" == '.' ]] && bodydot='true' && body222='false' # Stop building output when single dot line found.
    [[ "$body222" == 'true' ]] && [[ "$bodydot" == 'false' ]] && stripcard=$(echo "$stripcard" ; echo "$line")
    [[ "${line:0:3}" == "222" ]] && [[ $(echo "$line" | grep -c "$msgid") -eq 1 ]] && body222='true'
    done < <(echo "$nntpout" | head -n 16384)
    # Save the fetched article to inbox.
    echo "$stripcard" > "$zinbox/$passphrase"
    # Confirm and exit.
    DIE "NewsCard for ($passphrase) saved to inbox." && exit 0
    }
    # zombie : future refactoring
    # list_cards() { echo TODO unfinished function ; }
    # list_inbox() { echo TODO unfinished function ; }
    # list_archive() { echo TODO unfinished function ; }
    # list_sent() { echo TODO unfinished function ; }
    list_drafts() {
    # ensure valid argument
    foldermatch='false'
    for fpath in draft inbox queue sent
    do
    [[ "$arg2" == "$fpath" ]] && foldermatch='true'
    done # fpath
    [[ $foldermatch == 'false' ]] && ERR "[FM1] Invalid argument ($arg2). Try 'card help'."
    set_list_paths
    [[ "$arg2" == 'inbox' ]] && listfolder="$zinbox"
    [[ "$arg2" == 'draft' ]] && listfolder="$zdraft"
    [[ "$arg2" == 'queue' ]] && listfolder="$nzqqqq"
    [[ "$arg2" == 'sent' ]] && listfolder="$nzsent"
    # Drop to subshell and pipe to less
    (echo "* NEWSCARD * $arg2 * LISTING of $listfolder - 'Q' to quit."
    echo
    ls "$listfolder"
    echo) | less
    exit 0
    }
    # list drafts
    [[ "$mode" == "list" ]] && list_drafts && exit 0
    # check for existing file
    [[ "$mode" == "enc" ]] && check_filename
    [[ "$mode" == "tuff" ]] && check_filename
    # check the mode and run the matching operations
    [[ "$mode" == 'show' ]] && show_newscard && exit 0
    [[ "$mode" == "enc" ]] && makemsg && exit 0
    # unfinished
    #[[ "$mode" == "send" ]] && send_queue && exit 0 # zombie
    [[ "$mode" == "put" ]] && publish && exit 0
    [[ "$mode" == "get" ]] && fetch && exit 0
    [[ "$mode" == "tuff" ]] && makemsg && exit 0 # gen_tuff_params check inside makemsg
    # END.NEWSCARD
    # ▄▄▄ ▄▄▄ ▄▄▄ ▄▄▄ ▄▄ ▄▄▄
    # █ █ █ █ █ █ █ █ █▄▄ Byrl Raze Buckbriar # █▄█ █▄▄ █ █▀█ █▄▀ █▄▄ https://octade.net
    --
    = OCTADE = https://soc.octade.net/octade =
    --- Synchronet 3.20a-Win32 NewsLink 1.114