shadowsocks/shadowsocks-rust · error
`users[].password` should be base64 encoded
Error message
`users[].password` should be base64 encoded
What it means
Thrown when an EIP (extensible identity providers) entry under `users` in a server config has a `password` field that `ServerUser::with_encoded_key` cannot decode. For AEAD-2022 and multi-user setups, each user's key must be a base64-encoded key of the exact length the cipher requires; any non-base64 or wrong-length string fails with ErrorKind::Malformed.
Source
Thrown at crates/shadowsocks-service/src/config.rs:2308
let err = Error::new(
ErrorKind::Malformed,
"server config create failed",
Some(format!("{}", serr)),
);
return Err(err);
}
};
nsvr.set_source(server_source);
// Extensible Identity Header, Users
if let Some(users) = svr.users {
let mut user_manager = ServerUserManager::new();
for user in users {
let user = match ServerUser::with_encoded_key(user.name, &user.password) {
Ok(u) => u,
Err(..) => {
let err = Error::new(
ErrorKind::Malformed,
"`users[].password` should be base64 encoded",
None,
);
return Err(err);
}
};
user_manager.add_user(user);
}
nsvr.set_user_manager(user_manager);
}
match svr.mode {
Some(mode) => match mode.parse::<Mode>() {
Ok(mode) => nsvr.set_mode(mode),
Err(..) => {View on GitHub (pinned to 8eb0f0a65b)
Solutions
- Generate the user key with `openssl rand -base64 32` (or the byte size matching the cipher) and paste it exactly
- Ensure the string is valid standard base64 with correct padding
- Match the decoded key length to the cipher's key size (16/24/32 bytes depending on cipher)
- Keep server and user keys distinct — each user needs its own key
Example fix
// before
{"users": [{"name": "alice", "password": "plain-text-secret"}]}
// after
{"users": [{"name": "alice", "password": "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY="}]} Defensive patterns
Strategy: validation
Validate before calling
for u in &svr["users"].as_array().unwrap() {
let decoded = base64::decode(u["password"].as_str().unwrap()).expect("users[].password must be base64");
assert!(decoded.len() == 16 || decoded.len() == 32, "decoded key length {} invalid", decoded.len());
} Type guard
fn is_valid_user_key(pw: &str) -> bool {
base64::decode(pw).map(|k| k.len() == 16 || k.len() == 32).unwrap_or(false)
} Try / catch
match ServerUser::with_encoded_key(name, pw) {
Ok(u) => add(u),
Err(_) => eprintln!("user '{name}' key must be base64 of a 16/32-byte key"),
} Prevention
- Distribute keys only as base64 output of `openssl rand -base64 32`
- Avoid manual copy-paste truncation of long key strings
- Document that users[].password is a base64 key, not a login password
When it happens
Trigger: Loading a config with a `users: [{"name": ..., "password": ...}]` array where a user's `password` is not valid base64 or does not decode to the required key size (e.g. 32 bytes for 2022-blake3-aes-256-gcm).
Common situations: Users pasting plain-text passwords where base64 keys are required; truncated base64 strings from copy-paste; padding/whitespace corruption; generating keys with the wrong `openssl rand -base64` byte count for the cipher.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- `protocol` invalid
- invalid `mode`
- `server`, `server_port`, `method`, `password` must be provid
- invalid `tcp_weight`, must be in [0, 1]
- invalid `udp_weight`, must be in [0, 1]
AI-assisted analysis of shadowsocks/shadowsocks-rust@8eb0f0a65b (2026-09-09).
Data as JSON: /api/errors/6f83208738207eda.
Report an issue: GitHub.