1
1
use data_encoding:: BASE32_NOPAD ;
2
- use serde:: Deserialize ;
2
+ use serde:: { Deserialize , Serialize } ;
3
3
4
4
use crate :: otp:: { otp_algorithm:: OTPAlgorithm , otp_element:: OTPElement , otp_type:: OTPType } ;
5
5
6
- #[ derive( Deserialize ) ]
6
+ #[ derive( Serialize , Deserialize ) ]
7
7
pub struct FreeOTPPlusJson {
8
8
#[ serde( rename = "tokenOrder" ) ]
9
9
token_order : Vec < String > ,
10
10
tokens : Vec < FreeOTPElement > ,
11
11
}
12
12
13
- #[ derive( Deserialize ) ]
14
- struct FreeOTPElement {
15
- algo : String ,
16
- counter : u64 ,
17
- digits : u64 ,
13
+ impl FreeOTPPlusJson {
14
+ /// Creates a new instance of FreeOTPPlusJSON. Currently we clone the tokens label to retrieve the tokens order.
15
+ pub fn new ( tokens : Vec < FreeOTPElement > ) -> Self {
16
+ let token_order: Vec < String > = tokens
17
+ . iter ( )
18
+ . map ( |e| {
19
+ if e. issuer_ext . is_empty ( ) {
20
+ e. _label . clone ( )
21
+ } else {
22
+ format ! ( "{}:{}" , e. issuer_ext, e. _label)
23
+ }
24
+ } )
25
+ . collect ( ) ;
26
+
27
+ Self {
28
+ token_order,
29
+ tokens,
30
+ }
31
+ }
32
+ }
33
+
34
+ #[ derive( Serialize , Deserialize , PartialEq , Debug ) ]
35
+ pub struct FreeOTPElement {
36
+ pub algo : String ,
37
+ pub counter : u64 ,
38
+ pub digits : u64 ,
18
39
#[ serde( rename = "issuerExt" ) ]
19
- issuer_ext : String ,
40
+ pub issuer_ext : String ,
20
41
#[ serde( rename = "label" ) ]
21
- _label : String ,
22
- period : u64 ,
23
- secret : Vec < i8 > ,
42
+ pub _label : String ,
43
+ pub period : u64 ,
44
+ pub secret : Vec < i8 > ,
24
45
#[ serde( rename = "type" ) ]
25
- _type : String ,
46
+ pub _type : String ,
26
47
}
27
48
28
49
impl From < FreeOTPElement > for OTPElement {
@@ -49,20 +70,7 @@ impl From<FreeOTPElement> for OTPElement {
49
70
impl TryFrom < FreeOTPPlusJson > for Vec < OTPElement > {
50
71
type Error = String ;
51
72
fn try_from ( freeotp : FreeOTPPlusJson ) -> Result < Self , Self :: Error > {
52
- Ok ( freeotp
53
- . tokens
54
- . into_iter ( )
55
- . enumerate ( )
56
- . map ( |( i, mut token) | {
57
- token. _label = freeotp
58
- . token_order
59
- . get ( i)
60
- . unwrap_or ( & String :: from ( "No Label" ) )
61
- . to_owned ( ) ;
62
- token
63
- } )
64
- . map ( |e| e. into ( ) )
65
- . collect ( ) )
73
+ Ok ( freeotp. tokens . into_iter ( ) . map ( |e| e. into ( ) ) . collect ( ) )
66
74
}
67
75
}
68
76
@@ -78,7 +86,19 @@ fn encode_secret(secret: &[i8]) -> String {
78
86
79
87
#[ cfg( test) ]
80
88
mod tests {
81
- use super :: encode_secret;
89
+ use std:: path:: PathBuf ;
90
+
91
+ use crate :: {
92
+ importers:: { freeotp_plus:: FreeOTPElement , importer:: import_from_path} ,
93
+ otp:: { otp_algorithm:: OTPAlgorithm , otp_element:: OTPElement , otp_type:: OTPType } ,
94
+ } ;
95
+
96
+ use std:: fs;
97
+
98
+ use crate :: otp:: otp_element:: OTPDatabase ;
99
+ use color_eyre:: Result ;
100
+
101
+ use super :: { encode_secret, FreeOTPPlusJson } ;
82
102
83
103
#[ test]
84
104
fn test_secret_conversion ( ) {
@@ -92,4 +112,97 @@ mod tests {
92
112
String :: from( "3ZUNXX2SU6RZP4QFMS32YAILJFS2I2T2UZXYSHSX7IIMPAQZAC753NG2" )
93
113
) ;
94
114
}
115
+
116
+ #[ test]
117
+ fn test_conversion ( ) {
118
+ let imported = import_from_path :: < FreeOTPPlusJson > ( PathBuf :: from (
119
+ "test_samples/freeotp_plus_example1.json" ,
120
+ ) ) ;
121
+
122
+ assert_eq ! (
123
+ vec![
124
+ OTPElement {
125
+ secret: "AAAAAAAAAAAAAAAA" . to_string( ) ,
126
+ issuer: "Example2" . to_string( ) ,
127
+ label: "Label2" . to_string( ) ,
128
+ digits: 6 ,
129
+ type_: OTPType :: Totp ,
130
+ algorithm: OTPAlgorithm :: Sha1 ,
131
+ period: 30 ,
132
+ counter: None ,
133
+ pin: None
134
+ } ,
135
+ OTPElement {
136
+ secret: "AAAAAAAA" . to_string( ) ,
137
+ issuer: "Example1" . to_string( ) ,
138
+ label: "Label1" . to_string( ) ,
139
+ digits: 6 ,
140
+ type_: OTPType :: Totp ,
141
+ algorithm: OTPAlgorithm :: Sha256 ,
142
+ period: 30 ,
143
+ counter: None ,
144
+ pin: None
145
+ }
146
+ ] ,
147
+ imported. unwrap( )
148
+ )
149
+ }
150
+
151
+ #[ test]
152
+ fn test_freeotp_export ( ) {
153
+ // Arrange
154
+ let input_json: String = fs:: read_to_string ( PathBuf :: from ( "test_samples/cotp_input.json" ) )
155
+ . expect ( "Cannot read input file for test" ) ;
156
+ let input_cotp_database: OTPDatabase =
157
+ serde_json:: from_str ( input_json. as_str ( ) ) . expect ( "Cannot deserialize into input JSON" ) ;
158
+
159
+ // Act
160
+ let converted: Result < FreeOTPPlusJson > = ( & input_cotp_database) . try_into ( ) ;
161
+
162
+ // Assert
163
+ let free_otp = converted. unwrap ( ) ;
164
+
165
+ assert_eq ! (
166
+ vec![ "label1" . to_string( ) , "ciccio:label2" . to_string( ) ] ,
167
+ free_otp. token_order
168
+ ) ;
169
+
170
+ assert_eq ! (
171
+ vec![
172
+ FreeOTPElement {
173
+ algo: "SHA1" . to_string( ) ,
174
+ counter: 0 ,
175
+ digits: 6 ,
176
+ issuer_ext: String :: default ( ) ,
177
+ _label: "label1" . to_string( ) ,
178
+ period: 30 ,
179
+ secret: vec![
180
+ 7 , -40 , 73 , 126 , -112 , -25 , 37 , 28 , 72 , -39 , 115 , 50 , -127 , 46 , 74 , 117 ,
181
+ -40 , 124 , -109 , 58 , -19 , 54 , 35 , 117 , -120 , -106 , -40 , -39 , -116 , 107 ,
182
+ -123 , 127 , 111 , -93 , -71 , 6 , 92 , -116 , 31 , 4 , 103 , -59 , 75 , -106 , 57 , 54 ,
183
+ -3 , 104 , 103 , -26 , -57 , 59 , -69 , 98 , -16 , -102 , 91 , 89 , 98 , 90 , -100 , -21 ,
184
+ 44 , 28 , -105 , -45 , 92 , -128 , 82 , 30 , -23 , -105 , -30 , 91 , 17 , -51 , 24 , -7 ,
185
+ -61 , 75 , -38 , -116 , -122 , 106 , 79 , 37 , 82 , -62 , -125 , -30 , -27 , 116 , 116 ,
186
+ 82 , -55 , 72 , 87 , 41 , 15 , -25 , -27 , 65 , 6 , -104 , 49 , -26 , -111 , 10
187
+ ] ,
188
+ _type: "TOTP" . to_string( )
189
+ } ,
190
+ FreeOTPElement {
191
+ algo: "SHA256" . to_string( ) ,
192
+ counter: 3 ,
193
+ digits: 6 ,
194
+ issuer_ext: "ciccio" . to_string( ) ,
195
+ _label: "label2" . to_string( ) ,
196
+ period: 30 ,
197
+ secret: vec![
198
+ 35 , -75 , 13 , 47 , -2 , -128 , -100 , -27 , 64 , -115 , -72 , 14 , -78 , -122 , 88 , 62 ,
199
+ -32 , 57 , 37 , -111 , 90 , -70 , -58 , -15 , -113 , 111 , -94 , 91 , -90 , 90 , -91 , 61 ,
200
+ -9 , -23 , 54 , 4 , -31 , -93 , -8 , -9 , 27 , 125 , -21 , 112 , -80 , -30 , 64 , 46 , 10
201
+ ] ,
202
+ _type: "HOTP" . to_string( )
203
+ }
204
+ ] ,
205
+ free_otp. tokens
206
+ )
207
+ }
95
208
}
0 commit comments