use crate::{
error::Result,
message::{AuthType, NowAuthenticateMsg, NowAuthenticateTokenMsgOwned, NowString256, NowString64},
serialization::Encode,
};
use num_derive::FromPrimitive;
use std::str::FromStr;
#[derive(Encode, Decode, FromPrimitive, Debug, PartialEq, Clone, Copy)]
#[repr(u16)]
pub enum PFPMessageType {
Negotiate = 0x01,
Challenge = 0x02,
Response = 0x03,
}
#[derive(Encode, Decode, FromPrimitive, Debug, PartialEq, Clone, Copy)]
#[repr(u16)]
pub enum PFPMessageFlags {
NoChallenge = 0x0000,
Question = 0x0001,
}
#[derive(Debug, Clone, Encode, Decode)]
#[meta_enum = "PFPMessageType"]
pub enum NowAuthPFP {
Negotiate(NowAuthPFPNegotiate),
Challenge(NowAuthPFPChallenge),
Response(NowAuthPFPResponse),
}
impl NowAuthPFP {
pub fn new_owned_negotiate_token<'a>(friendly_name: &str, friendly_text: &str) -> Result<NowAuthenticateMsg<'a>> {
let negotiate_token = NowAuthPFPNegotiate::new(
NowString64::from_str(friendly_name)?,
NowString256::from_str(friendly_text)?,
)
.encode()?;
Ok(NowAuthenticateTokenMsgOwned::new(AuthType::PFP, negotiate_token).into())
}
}
#[derive(Decode, Encode, Debug, Clone)]
pub struct NowAuthPFPNegotiate {
pub subtype: PFPMessageType,
pub flags: PFPMessageFlags,
pub friendly_name: NowString64,
pub friendly_text: NowString256,
}
impl NowAuthPFPNegotiate {
pub const MIN_REQUIRED_SIZE: usize = 6;
pub fn new(friendly_name: NowString64, friendly_text: NowString256) -> Self {
Self {
subtype: PFPMessageType::Negotiate,
flags: PFPMessageFlags::Question,
friendly_name,
friendly_text,
}
}
}
#[derive(Encode, Decode, Debug, Clone)]
pub struct NowAuthPFPChallenge {
pub subtype: PFPMessageType,
pub flags: PFPMessageFlags,
pub question: NowString256,
}
impl NowAuthPFPChallenge {
pub const MIN_REQUIRED_SIZE: usize = 5;
pub fn new_without_question() -> Self {
Self {
subtype: PFPMessageType::Challenge,
flags: PFPMessageFlags::NoChallenge,
question: NowString256::new_empty(),
}
}
pub fn new_with_question(question: NowString256) -> Self {
Self {
subtype: PFPMessageType::Challenge,
flags: PFPMessageFlags::Question,
question,
}
}
}
#[derive(Decode, Encode, Debug, Clone)]
pub struct NowAuthPFPResponse {
pub subtype: PFPMessageType,
pub flags: PFPMessageFlags,
pub answer: NowString256,
}
impl NowAuthPFPResponse {
pub const MIN_REQUIRED_SIZE: usize = 5;
pub fn new(answer: NowString256) -> Self {
Self {
subtype: PFPMessageType::Response,
flags: PFPMessageFlags::Question,
answer,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::serialization::{Decode, Encode};
use std::str::FromStr;
#[rustfmt::skip]
const PFP_NEGOTIATE_TOKEN: [u8; 26] = [
0x01, 0x00, 0x01, 0x00, 0x0a, 0x4a, 0x6f, 0x68, 0x6e, 0x6e, 0x79, 0x20, 0x44, 0x6f, 0x65, 0x00, 0x08, 0x49, 0x74, 0x27, 0x73, 0x20, 0x6d, 0x65, 0x2e,
0x00, ];
#[test]
fn negotiate_decoding() {
let msg = NowAuthPFP::decode(&PFP_NEGOTIATE_TOKEN).unwrap();
if let NowAuthPFP::Negotiate(msg) = msg {
assert_eq!(msg.subtype, PFPMessageType::Negotiate);
assert_eq!(msg.flags, PFPMessageFlags::Question);
assert_eq!(msg.friendly_name, "Johnny Doe");
assert_eq!(msg.friendly_text, "It's me.");
} else {
panic!("Expected a negotiate message, found {:?}", msg);
}
}
#[test]
fn negotiate_encoding() {
let msg = NowAuthPFPNegotiate::new(
NowString64::from_str("Johnny Doe").unwrap(),
NowString256::from_str("It's me.").unwrap(),
);
assert_eq!(msg.encode().unwrap(), PFP_NEGOTIATE_TOKEN.to_vec());
}
#[rustfmt::skip]
const PFP_CHALLENGE_TOKEN: [u8; 18] = [
0x02, 0x00, 0x01, 0x00, 0x0c, 0x48, 0x6f, 0x77, 0x20, 0x61, 0x72, 0x65, 0x20, 0x79, 0x6f, 0x75, 0x3f,
0x00, ];
#[rustfmt::skip]
const PFP_NO_CHALLENGE_TOKEN: [u8; 6] = [
0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
];
#[test]
fn challenge_decoding() {
let msg = NowAuthPFP::decode(&PFP_CHALLENGE_TOKEN).unwrap();
if let NowAuthPFP::Challenge(msg) = msg {
assert_eq!(msg.subtype, PFPMessageType::Challenge);
assert_eq!(msg.flags, PFPMessageFlags::Question);
assert_eq!(msg.question, "How are you?");
} else {
panic!("Expected a challenge message, found {:?}", msg);
}
}
#[test]
fn no_challenge_decoding() {
let msg = NowAuthPFP::decode(&PFP_NO_CHALLENGE_TOKEN).unwrap();
if let NowAuthPFP::Challenge(msg) = msg {
assert_eq!(msg.subtype, PFPMessageType::Challenge);
assert_eq!(msg.flags, PFPMessageFlags::NoChallenge);
assert_eq!(msg.question, "");
} else {
panic!("Expected a challenge message, found {:?}", msg);
}
}
#[test]
fn challenge_encoding() {
let msg = NowAuthPFPChallenge::new_with_question(NowString256::from_str("How are you?").unwrap());
assert_eq!(msg.encode().unwrap(), PFP_CHALLENGE_TOKEN.to_vec());
}
#[test]
fn no_challenge_encoding() {
let msg = NowAuthPFPChallenge::new_without_question();
assert_eq!(PFP_NO_CHALLENGE_TOKEN.to_vec(), msg.encode().unwrap());
}
#[rustfmt::skip]
const PFP_RESPONSE_TOKEN: [u8; 12] = [
0x03, 0x00, 0x01, 0x00, 0x6, 0xe5, 0x85, 0x83, 0xe6, 0xb0, 0x97, 0x00,
];
#[test]
fn response_decoding() {
let msg = NowAuthPFP::decode(&PFP_RESPONSE_TOKEN).unwrap();
if let NowAuthPFP::Response(msg) = msg {
assert_eq!(msg.subtype, PFPMessageType::Response);
assert_eq!(msg.flags, PFPMessageFlags::Question);
assert_eq!(msg.answer, "元気");
} else {
panic!("Expected a response message, found {:?}", msg);
}
}
#[test]
fn response_encoding() {
let msg = NowAuthPFPResponse::new(NowString256::from_str("元気").unwrap());
assert_eq!(msg.encode().unwrap(), PFP_RESPONSE_TOKEN.to_vec());
}
}