1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
pub mod private_key;
pub mod public_key;
pub mod signature;

use std::io::{self, Error, ErrorKind};

use crate::codec::serde::hex_0x_bytes::Hex0xBytes;
use serde::{Deserialize, Serialize};
use serde_with::serde_as;

/// ref. "avalanchego"/vms/platformvm/signer.ProofOfPossession"
/// ref. <https://docs.avax.network/build/avalanchego-apis/info/#infogetnodeid>
#[serde_as]
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq, Clone)]
pub struct ProofOfPossession {
    #[serde(rename = "publicKey")]
    #[serde_as(as = "Hex0xBytes")]
    pub public_key: Vec<u8>,
    #[serde(rename = "proofOfPossession")]
    #[serde_as(as = "Hex0xBytes")]
    pub proof_of_possession: Vec<u8>,

    #[serde(skip)]
    pub pubkey: Option<public_key::Key>,
}

impl Default for ProofOfPossession {
    fn default() -> Self {
        Self::default()
    }
}

impl ProofOfPossession {
    pub fn default() -> Self {
        Self {
            public_key: Vec::new(),
            proof_of_possession: Vec::new(),
            pubkey: None,
        }
    }
}

impl ProofOfPossession {
    pub fn new(public_key: &[u8], proof_of_possession: &[u8]) -> io::Result<Self> {
        let pubkey = public_key::Key::from_bytes(public_key)?;
        let sig = signature::Sig::from_bytes(proof_of_possession)?;

        if !pubkey.verify_proof_of_possession(public_key, &sig) {
            return Err(Error::new(
                ErrorKind::Other,
                "failed verify_proof_of_possession",
            ));
        }

        Ok(Self {
            public_key: public_key.to_vec(),
            proof_of_possession: proof_of_possession.to_vec(),
            pubkey: Some(pubkey),
        })
    }

    /// ref. "avalanchego"/vms/platformvm/signer.ProofOfPossession.Verify"
    pub fn verify(&self) -> io::Result<bool> {
        let pubkey = public_key::Key::from_bytes(&self.public_key)?;
        let pubkey_bytes = pubkey.to_compressed_bytes();

        let sig = signature::Sig::from_bytes(&self.proof_of_possession)?;

        Ok(pubkey.verify_proof_of_possession(&pubkey_bytes, &sig))
    }

    pub fn load_pubkey(&self) -> io::Result<public_key::Key> {
        // ref. "avalanchego/vms/platformvm/signer.ProofOfPossession.UnmarshalJSON"
        let pubkey = public_key::Key::from_bytes(&self.public_key)?;
        Ok(pubkey)
    }
}

/// RUST_LOG=debug cargo test --package avalanche-types --all-features --lib -- key::bls::test_proof_of_possession --exact --show-output
#[test]
fn test_proof_of_possession() {
    let _ = env_logger::builder()
        .filter_level(log::LevelFilter::Info)
        .is_test(true)
        .try_init();

    let sk = private_key::Key::generate().unwrap();
    let pubkey = sk.to_public_key();

    let msg = random_manager::secure_bytes(50).unwrap();
    let sig = sk.sign(&msg);
    assert!(sig.verify(&msg, &pubkey));
    assert!(!sig.verify_proof_of_possession(&msg, &pubkey));

    let sig_pop = sk.sign_proof_of_possession(&msg);
    assert!(!sig_pop.verify(&msg, &pubkey));
    assert!(sig_pop.verify_proof_of_possession(&msg, &pubkey));

    let b = sk.to_bytes();
    let sk2 = private_key::Key::from_bytes(&b).unwrap();
    assert_eq!(sk.to_bytes(), sk2.to_bytes());

    let key_path = random_manager::tmp_path(10, None).unwrap();
    let generated_key = private_key::Key::generate_to_file(&key_path).unwrap();
    let loaded_key = private_key::Key::from_file(&key_path).unwrap();
    assert_eq!(generated_key.to_bytes(), loaded_key.to_bytes());

    let (loaded_key_2, generated) = private_key::Key::load_or_generate(&key_path).unwrap();
    assert!(!generated);
    assert_eq!(loaded_key.to_bytes(), loaded_key_2.to_bytes());

    std::fs::remove_file(&key_path).unwrap();

    let pop = sk.to_proof_of_possession();
    log::info!(
        "proof-of-possession: {}",
        serde_json::to_string_pretty(&pop).unwrap()
    );
    assert!(pop.verify().unwrap());

    let pop =
        ProofOfPossession::new(&pubkey.to_compressed_bytes(), &pop.proof_of_possession).unwrap();
    assert!(pop.verify().unwrap());
    assert_eq!(pop.public_key, pubkey.to_compressed_bytes());
    assert_eq!(
        pop.pubkey.unwrap().to_compressed_bytes(),
        pubkey.to_compressed_bytes()
    );
}