Skip to main content

dark_crystal_web3_core/secret/
mod.rs

1//! Encode secrets as various types
2
3use crate::secret::bip39::{bytes_to_mnemonic, mnemonic_to_bytes};
4use std::str::Utf8Error;
5pub mod bip39;
6
7/// A secret with the first byte representing its type
8/// allowing for up to 256 types
9#[derive(Debug, PartialEq)]
10pub enum Secret {
11    Raw(Vec<u8>),  // 0
12    Utf8(String),  // 1
13    Bip39(String), // 2
14}
15// TODO Labelled(String, Vec<u8>), // 3
16
17impl Secret {
18    /// Given an encoded secret, determine its type
19    pub fn from_bytes(input: Vec<u8>) -> Result<Secret, EncodingError> {
20        match input[0] {
21            0 => Ok(Secret::Raw(input[1..].to_vec())),
22            1 => Ok(Secret::Utf8(std::str::from_utf8(&input[1..])?.to_string())),
23            2 => Ok(Secret::Bip39(bytes_to_mnemonic(&input[1..].to_vec())?)),
24            _ => Err(EncodingError {}),
25        }
26    }
27
28    /// Encode a secret
29    pub fn to_bytes(&self) -> Result<Vec<u8>, EncodingError> {
30        fn add_type(type_code: u8, input: Vec<u8>) -> Vec<u8> {
31            let mut output: Vec<u8> = vec![type_code];
32            output.extend(input);
33            output
34        }
35
36        match self {
37            Secret::Raw(bytes) => Ok(add_type(0, bytes.to_vec())),
38            Secret::Utf8(string) => Ok(add_type(1, string.as_bytes().to_vec())),
39            Secret::Bip39(string) => Ok(add_type(2, mnemonic_to_bytes(string)?)),
40        }
41    }
42
43    pub fn detect_from_string(input: String) -> Secret {
44        match mnemonic_to_bytes(&input) {
45            Ok(_) => Secret::Bip39(input),
46            Err(_) => match hex::decode(&input) {
47                Ok(bytes) => Secret::Raw(bytes),
48                Err(_) => Secret::Utf8(input),
49            },
50        }
51    }
52}
53
54/// Error given when encoding or decoding a secret
55#[derive(Debug)]
56pub struct EncodingError {}
57
58impl From<Utf8Error> for EncodingError {
59    fn from(_error: std::str::Utf8Error) -> Self {
60        EncodingError {}
61    }
62}
63
64impl From<::bip39::Error> for EncodingError {
65    fn from(_error: ::bip39::Error) -> Self {
66        EncodingError {}
67    }
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn encode_secrets() {
76        assert_eq!(
77            vec![0, 98, 108, 97, 104],
78            Secret::Raw(b"blah".to_vec()).to_bytes().unwrap()
79        );
80
81        assert_eq!(
82            vec![1, 98, 108, 97, 104],
83            Secret::Utf8("blah".to_string()).to_bytes().unwrap()
84        );
85
86        assert_eq!(
87            vec![2, 208, 109, 216, 221, 151, 58, 13, 74, 112, 181, 62, 174, 181, 203, 225, 153],
88            Secret::Bip39(
89                "spare human danger common patch pioneer security pond push purity wear crane"
90                    .to_string()
91            )
92            .to_bytes()
93            .unwrap()
94        );
95    }
96
97    #[test]
98    fn decode_secrets() {
99        assert_eq!(
100            Secret::from_bytes(vec![0, 98, 108, 97, 104]).unwrap(),
101            Secret::Raw(b"blah".to_vec())
102        );
103
104        assert_eq!(
105            Secret::from_bytes(vec![1, 98, 108, 97, 104]).unwrap(),
106            Secret::Utf8("blah".to_string())
107        );
108
109        assert_eq!(
110            Secret::from_bytes(vec![
111                2, 208, 109, 216, 221, 151, 58, 13, 74, 112, 181, 62, 174, 181, 203, 225, 153
112            ])
113            .unwrap(),
114            Secret::Bip39(
115                "spare human danger common patch pioneer security pond push purity wear crane"
116                    .to_string()
117            )
118        );
119    }
120
121    #[test]
122    fn detect_string() {
123        assert_eq!(
124            Secret::detect_from_string("blah".to_string()),
125            Secret::Utf8("blah".to_string())
126        );
127
128        assert_eq!(
129            Secret::detect_from_string("deadbeef".to_string()),
130            Secret::Raw(hex::decode("deadbeef").unwrap())
131        );
132
133        assert_eq!(
134            Secret::detect_from_string("dead beef".to_string()),
135            Secret::Utf8("dead beef".to_string())
136        );
137
138        assert_eq!(
139            Secret::detect_from_string(
140                "spare human danger common patch pioneer security pond push purity wear crane"
141                    .to_string()
142            ),
143            Secret::Bip39(
144                "spare human danger common patch pioneer security pond push purity wear crane"
145                    .to_string()
146            )
147        );
148    }
149}