3
3
Version: 1.0.0
4
4
Title: "Codec: SAFE"
5
5
Author: "Oldes"
6
- History: [10-Jul-2022 "Oldes" "Initial version" ]
6
+ History: [
7
+ 10-Jul-2022 "Oldes" "Initial version"
8
+ 12-Jul-2022 "Oldes" "Included SAFE scheme"
9
+ ]
7
10
File: https://raw.githubusercontent.com/Oldes/Rebol3/master/src/mezz/codec-safe.reb
11
+ TODO: [
12
+ "The scheme could require to enter the password again after some time of inactivity."
13
+ ]
8
14
]
9
15
10
16
register-codec [
@@ -16,7 +22,7 @@ register-codec [
16
22
encode : function [
17
23
data [any-type! ]
18
24
/key
19
- password [any-string! binary! ]
25
+ password [any-string! binary! none! ]
20
26
/as
21
27
cipher [word! ]
22
28
] [
@@ -73,10 +79,11 @@ register-codec [
73
79
]
74
80
75
81
decode : function [
76
- data [binary! ]
82
+ data [binary! file! url! ]
77
83
/key
78
- password [any-string! binary! ]
84
+ password [any-string! binary! none! ]
79
85
] [
86
+ unless binary? data [ data: read /binary data ]
80
87
unless parse data [id data: to end][return none]
81
88
binary/read data [
82
89
flags: UI16
@@ -120,9 +127,130 @@ register-codec [
120
127
ask/hide "Enter SAFE Password: "
121
128
]
122
129
]
123
-
124
- ;probe decode encode #(a: 1)
125
- ;probe decode encode #(a: 1 b: 2)
126
- ;probe decode encode #{DEAD}
127
130
]
128
131
132
+
133
+ sys/make-scheme [
134
+ title: "Persistent key/value storage"
135
+ name: 'safe
136
+ actor: [
137
+ open : func [ port [port! ] /local spec host] [
138
+ spec: port/spec
139
+ spec/ref: rejoin either /only host: select spec 'host [
140
+ https:// host
141
+ select spec 'path
142
+ select spec 'target %""
143
+ ][
144
+ any [select spec 'path system/options/home ]
145
+ any [select spec 'target %.safe ]
146
+ ]
147
+ if %.safe <> suffix? spec/ref [
148
+ append spec/ref %.safe
149
+ ]
150
+
151
+ port/data: object [
152
+ data: none
153
+ pass: any [
154
+ select spec 'pass
155
+ ask/hide "Enter password: "
156
+ ]
157
+ file: port/spec/ref
158
+ date: none
159
+ get : func [ key] [select data :key ]
160
+ set : func [ key [word! ] val [any-type! ]] [put data :key :val ]
161
+ rem : func [ key] [remove/key data :key #[unset!]]
162
+ load : does [
163
+ date: modified? file
164
+ data: system/codecs/safe/decode/key :file :pass
165
+ unless data [
166
+ print [as-purple "*** Failed to decrypt data from:" as-red file]
167
+ ]
168
+ ]
169
+ save : does [
170
+ if url? file [
171
+ print as-purple "*** Saving to URL is not yet implemented!"
172
+ exit
173
+ ]
174
+ ;@@ TODO: prevent collision when 2 processes tries to write in the same time!
175
+ write /binary file system/codecs/safe/encode/key :data :pass
176
+ date: modified? file
177
+ ]
178
+ sync : func [ /close /local modf] [
179
+ if data [
180
+ ;; There are already some data..
181
+ case [
182
+ not exists? file [ save ]
183
+ date > modf: modified? file [ save ]
184
+ date < modf [ load ]
185
+ ]
186
+ data
187
+ ]
188
+ case [
189
+ close [
190
+ data: date: pass: none
191
+ ]
192
+ none? data [
193
+ ;; There are no data yet, so load it or make a new map!
194
+ either exists? file [ load ][
195
+ data: make map! 4
196
+ save
197
+ ]
198
+ ]
199
+ ]
200
+ file
201
+ ]
202
+ open? : does [ map? data ]
203
+ change-pass : func [ new [string! binary! ]] [
204
+ either pass = ask/hide "Old password: " [
205
+ pass: new
206
+ date: now /precise
207
+ sync
208
+ true
209
+ ][
210
+ sys/log/error 'REBOL "Password validation failed!"
211
+ false
212
+ ]
213
+ ]
214
+
215
+ sync
216
+
217
+ protect /words/hide [data pass load save]
218
+ protect /words [get set rem sync open? change-pass]
219
+ ]
220
+
221
+ ; make only these required values availeble in the spec:
222
+ set port/spec: object [title: scheme: ref: none] spec
223
+
224
+ ;- It is not possible to protect data so far, because of:
225
+ ;- https://github.com/Oldes/Rebol-issues/issues/1148
226
+ ;protect/words port/data
227
+ port
228
+ ]
229
+ ; We must use the inner functions bellow, because the inner data are hidden!
230
+ open? : func [ port] [
231
+ port/data/open?
232
+ ]
233
+ close : func [ port] [
234
+ port/data/sync/close
235
+ ]
236
+ put:
237
+ poke : func [ port key value] [
238
+ port/data/date: now /precise
239
+ port/data/set :key :value
240
+ ]
241
+ pick:
242
+ select : func [ port key] [
243
+ port/data/get :key
244
+ ]
245
+ remove : func [ port /part range /key key-arg] [
246
+ port/data/date: now /precise
247
+ port/data/rem :key-arg
248
+ ]
249
+ update : func [ port] [
250
+ port/data/sync
251
+ ]
252
+ modify : func [ port field value] [
253
+ if field = 'password [port/data/change-pass :value ]
254
+ ]
255
+ ]
256
+ ]
0 commit comments