Skip to content

Commit c0ff574

Browse files
committed
FEAT: included safe scheme as a persistent encrypted key/value data storage
1 parent 0bf3892 commit c0ff574

File tree

1 file changed

+136
-8
lines changed

1 file changed

+136
-8
lines changed

src/mezz/codec-safe.reb

+136-8
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ REBOL [
33
Version: 1.0.0
44
Title: "Codec: SAFE"
55
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+
]
710
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+
]
814
]
915

1016
register-codec [
@@ -16,7 +22,7 @@ register-codec [
1622
encode: function [
1723
data [any-type!]
1824
/key
19-
password [any-string! binary!]
25+
password [any-string! binary! none!]
2026
/as
2127
cipher [word!]
2228
][
@@ -73,10 +79,11 @@ register-codec [
7379
]
7480

7581
decode: function [
76-
data [binary!]
82+
data [binary! file! url!]
7783
/key
78-
password [any-string! binary!]
84+
password [any-string! binary! none!]
7985
][
86+
unless binary? data [ data: read/binary data ]
8087
unless parse data [id data: to end][return none]
8188
binary/read data [
8289
flags: UI16
@@ -120,9 +127,130 @@ register-codec [
120127
ask/hide "Enter SAFE Password: "
121128
]
122129
]
123-
124-
;probe decode encode #(a: 1)
125-
;probe decode encode #(a: 1 b: 2)
126-
;probe decode encode #{DEAD}
127130
]
128131

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

Comments
 (0)