Ensure this isn't a computer game by itself.

Require a signed age-verification.yml to complete the server part
of the game, so it can't be used by anyone underage.
This commit is contained in:
Condorra 2022-12-18 02:30:30 +11:00
parent 47e47345cc
commit 7dd8b05855
6 changed files with 211 additions and 5 deletions

122
Cargo.lock generated
View File

@ -36,6 +36,12 @@ version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
[[package]]
name = "base64"
version = "0.20.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -46,11 +52,13 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
name = "blastmud_game" name = "blastmud_game"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"base64 0.20.0",
"blastmud_interfaces", "blastmud_interfaces",
"deadpool-postgres", "deadpool-postgres",
"futures", "futures",
"log", "log",
"nix", "nix",
"ring",
"serde", "serde",
"serde_yaml", "serde_yaml",
"simple_logger", "simple_logger",
@ -97,6 +105,12 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "bumpalo"
version = "3.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
[[package]] [[package]]
name = "byteorder" name = "byteorder"
version = "1.4.3" version = "1.4.3"
@ -109,6 +123,12 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c" checksum = "dfb24e866b15a1af2a1b663f10c6b6b8f397a84aadb828f12e5b289ec23a3a3c"
[[package]]
name = "cc"
version = "1.0.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
[[package]] [[package]]
name = "cfg-if" name = "cfg-if"
version = "1.0.0" version = "1.0.0"
@ -380,6 +400,15 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc" checksum = "4217ad341ebadf8d8e724e264f13e593e0648f5b3e94b3896a5df283be015ecc"
[[package]]
name = "js-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
dependencies = [
"wasm-bindgen",
]
[[package]] [[package]]
name = "lazy_static" name = "lazy_static"
version = "1.4.0" version = "1.4.0"
@ -601,7 +630,7 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "878c6cbf956e03af9aa8204b407b9cbf47c072164800aa918c516cd4b056c50c" checksum = "878c6cbf956e03af9aa8204b407b9cbf47c072164800aa918c516cd4b056c50c"
dependencies = [ dependencies = [
"base64", "base64 0.13.1",
"byteorder", "byteorder",
"bytes", "bytes",
"fallible-iterator", "fallible-iterator",
@ -694,6 +723,21 @@ version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0" checksum = "4389f1d5789befaf6029ebd9f7dac4af7f7e3d61b69d4f30e2ac02b57e7712b0"
[[package]]
name = "ring"
version = "0.16.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3053cf52e236a3ed746dfc745aa9cacf1b791d846bdaf412f60a8d7d6e17c8fc"
dependencies = [
"cc",
"libc",
"once_cell",
"spin",
"untrusted",
"web-sys",
"winapi",
]
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.0" version = "0.4.0"
@ -828,6 +872,12 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]] [[package]]
name = "static_assertions" name = "static_assertions"
version = "1.1.0" version = "1.1.0"
@ -1052,6 +1102,12 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68" checksum = "c1e5fa573d8ac5f1a856f8d7be41d390ee973daf97c806b2c1a465e4e1406e68"
[[package]]
name = "untrusted"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.2.2" version = "1.2.2"
@ -1074,6 +1130,70 @@ version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]]
name = "wasm-bindgen"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
dependencies = [
"bumpalo",
"log",
"once_cell",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
[[package]]
name = "web-sys"
version = "0.3.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

View File

@ -1,10 +1,45 @@
# Blastmud # Blastmud
A Rust-based mud (multi-user text-based game). Unlike many muds, it is designed to be completely A component of a Rust-based mud (multi-user text-based game). Unlike many muds, it is designed to
Free software, with the game core written in Rust rather than in any form of softcode. Only user be completely Free software (other than an age-verification file - see below), with the game
data forms part of the database. Even the map is programmed in a normal text editor, and can be core written in Rust rather than in any form of softcode. Only user data forms part of the database.
Even the map is programmed in a normal text editor, and can be
tested locally before being deployed to the game. tested locally before being deployed to the game.
# Age verification file
The Blastmud game is for adults only (18 years of age or older). In order to make a complete game, three
components are required:
* This game server codebase - which is publicly available and shareable under a permissive (3-clause BSD-style license). It isn't playable as a game by itself.
* A client. Openly available software such as telnet, tintin++, or mudlet can be used as this component.
* A closed-source age verification file, to be placed as `age-verification.yml` alongside the `gameserver.conf` file. This file is copyrighted with all rights reserved (except for the use by the person it is intended for to run the game) and cannot legally be given to anyone else. The initial author of Blastmud intends to provide an `age-verification.yml` file to anyone I am satisfied is not a minor.
## Why does a Free/Open Source project deliberately include a requirement for a non-Open Source file?
In the jurisdiction where the initial author is based, it is illegal to distribute unclassified or R18+ classified games (defined as playable software / data / some combination of it) to people under 18. Restricting access to all components of the game would be an impediment for easy collaboration on the game.
So a decision was made to only distribute a non-playable Free / Open Source component without restrictions (and to ensure this non-playable component doesn't, by itself, meet the definition of either a computer game or a submittable publication).
## I obtained an `age-verification.yml` from the initial author - can I share it / publish it?
No, this file is licensed solely to you and it is a breach of copyright law to publish it without consent from the initial author. A takedown request for the material might be sent, the shared `age-verification.yml` might be revoked in future versions of the server codebase, and you could even be sued for copyright infringement.
Depending on your jurisdiction, publishing a complete game (including `age-verification.yml`) to people who are under 18 could also be a crime.
If you attempt to use the official Blastmud GitHub project (or any other resources) to share `age-verification.yml` (e.g. through issues or pull requests), the material will be deleted and you will be blocked from further interaction with the project (unless we are satisfied it was accidental).
You are allowed to put it on a computer system / server where it is only accessible to a limited number of people known to you, as long as you have verified all those people are 18 or over, and know not to further distribute the file.
## Can I change / remove the code so it doesn't need `age-verification.yml`?
The license for Blastmud allows you to change the code and redistribute your changes. If you are forking Blastmud to create your own game engine, you could change
the age verification keypair or entirely remove the code. You may not call such a modified game Blastmud. Please be aware that if
you modify the code to create a complete computer game, in some jurisdictions you might have to get your fork classified, and might
have legal obligations not to distribute it to anyone under a certain age.
Regarding the use of official Blastmud resources such as our GitHub project and game server instance: to ensure minors are protected, you must not post versions of Blastmud that disable the checking of `age-verification.yml` (or post any other complete unclassified game or game that is unsuitable for minors of any age), nor post patches, pull requests, or instructions for doing the same. You may be blocked from further interaction with the project if you do this (unless we are satisfied it was accidental).
# Architecture # Architecture
Blastmud consists of the following main components: Blastmud consists of the following main components:

View File

@ -6,11 +6,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
base64 = "0.20.0"
blastmud_interfaces = { path = "../blastmud_interfaces" } blastmud_interfaces = { path = "../blastmud_interfaces" }
deadpool-postgres = { version = "0.10.3", features = ["serde"] } deadpool-postgres = { version = "0.10.3", features = ["serde"] }
futures = "0.3.25" futures = "0.3.25"
log = "0.4.17" log = "0.4.17"
nix = "0.26.1" nix = "0.26.1"
ring = "0.16.20"
serde = { version = "1.0.150", features = ["derive", "serde_derive"] } serde = { version = "1.0.150", features = ["derive", "serde_derive"] }
serde_yaml = "0.9.14" serde_yaml = "0.9.14"
simple_logger = "4.0.0" simple_logger = "4.0.0"

39
blastmud_game/src/av.rs Normal file
View File

@ -0,0 +1,39 @@
use std::fs;
use std::error::Error;
use serde::Deserialize;
use ring::signature;
use base64;
use log::info;
#[derive(Deserialize)]
struct AV {
copyright: String,
serial: u64,
cn: String,
assertion: String,
sig: String
}
static KEY_BYTES: [u8;65] = [
0x04, 0x4f, 0xa0, 0x8b, 0x32, 0xa7, 0x7f, 0xc1, 0x0a, 0xfc, 0x51, 0x95, 0x93, 0x57, 0x05,
0xb3, 0x0f, 0xad, 0x16, 0x05, 0x3c, 0x7c, 0xfc, 0x02, 0xd2, 0x7a, 0x63, 0xff, 0xd3, 0x09,
0xaa, 0x5b, 0x78, 0xfe, 0xa8, 0xc2, 0xc3, 0x02, 0xc2, 0xe6, 0xaf, 0x81, 0xc7, 0xa3, 0x03,
0xfa, 0x4d, 0xf1, 0xf9, 0xfc, 0x0a, 0x36, 0xef, 0x6b, 0x1e, 0x9d, 0xce, 0x6e, 0x60, 0xc6,
0xa8, 0xb3, 0x02, 0x35, 0x7e
];
pub fn check() -> Result<(), Box<dyn Error>> {
let av: AV = serde_yaml::from_str(&fs::read_to_string("age-verification.yml")?).
map_err(|error| Box::new(error) as Box<dyn Error>)?;
if av.copyright != "This file is protected by copyright and may not be used or reproduced except as authorised by the copyright holder. All rights reserved." ||
av.assertion != "age>=18" {
Err(Box::<dyn Error>::from("Invalid age-verification.yml"))?;
}
let sign_text = format!("cn={};{};serial={}", av.cn, av.assertion, av.serial);
let key: signature::UnparsedPublicKey<&[u8]> =
signature::UnparsedPublicKey::new(&signature::ECDSA_P256_SHA256_ASN1, &KEY_BYTES);
info!("Checking sign_text: {}", sign_text);
key.verify(&sign_text.as_bytes(), &base64::decode(av.sig)?)
.map_err(|e| Box::<dyn Error>::from(format!("Invalid age-verification.yml signature: {}", e)))
}

View File

@ -2,8 +2,13 @@ use tokio_postgres::config::Config as PgConfig;
use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod}; use deadpool_postgres::{Manager, ManagerConfig, Pool, RecyclingMethod};
use std::error::Error; use std::error::Error;
use std::str::FromStr; use std::str::FromStr;
use uuid::Uuid;
use tokio_postgres::NoTls; use tokio_postgres::NoTls;
pub async fn record_listener_ping(_listener: Uuid, _pool: Pool) {
// pool.get().await?.query("");
}
pub fn start_pool(connstr: &str) -> Result<Pool, Box<dyn Error>> { pub fn start_pool(connstr: &str) -> Result<Pool, Box<dyn Error>> {
let mgr_config = ManagerConfig { let mgr_config = ManagerConfig {
recycling_method: RecyclingMethod::Fast recycling_method: RecyclingMethod::Fast

View File

@ -1,7 +1,7 @@
use serde::Deserialize; use serde::Deserialize;
use std::fs; use std::fs;
use std::error::Error; use std::error::Error;
use log::{info, LevelFilter}; use log::{info, error, LevelFilter};
use simple_logger::SimpleLogger; use simple_logger::SimpleLogger;
use tokio::signal::unix::{signal, SignalKind}; use tokio::signal::unix::{signal, SignalKind};
@ -9,6 +9,7 @@ mod db;
mod listener; mod listener;
mod message_handler; mod message_handler;
mod version_cutover; mod version_cutover;
mod av;
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
struct Config { struct Config {
@ -26,6 +27,10 @@ fn read_latest_config() -> Result<Config, Box<dyn Error>> {
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap(); SimpleLogger::new().with_level(LevelFilter::Info).init().unwrap();
av::check().or_else(|e| -> Result<(), Box<dyn Error>> {
error!("Couldn't verify age-verification.yml - this is not a complete game. Check README.md: {}", e);
Err(e)
})?;
let config = read_latest_config()?; let config = read_latest_config()?;
let pool = db::start_pool(&config.database_conn_string)?; let pool = db::start_pool(&config.database_conn_string)?;