Harden browser FFI and secure store parsing

ober

873da2789f9879b497e961214d1273cfa271360e

diff --git a/scheme/browser/secure-store.ss b/scheme/browser/secure-store.ss
index ad960d3..3eda7ea 100644
--- a/scheme/browser/secure-store.ss
+++ b/scheme/browser/secure-store.ss
@@ -87,6 +87,12 @@
                         (* 256 (bytevector-u8-ref b (+ o 2)))
                         (bytevector-u8-ref b (+ o 3))))
 
+  (def (require-bytes who b off len)
+    (unless (and (>= off 0)
+                 (>= len 0)
+                 (<= (+ off len) (bytevector-length b)))
+      (error who "truncated payload")))
+
   ;; --- bookmark (de)serialization ---
   ;; pairs :: list of (url-string . title-string)
   (def (serialize-pairs pairs)
@@ -98,17 +104,29 @@
            pairs)))
 
   (def (deserialize-pairs b)
-    (let ([n (u32-ref b 0)])
+    (require-bytes 'deserialize-pairs b 0 4)
+    (let ([n (u32-ref b 0)]
+          [blen (bytevector-length b)])
       (let loop ([i 0] [off 4] [acc '()])
-        (if (= i n) (reverse acc)
-            (let* ([ulen (u16-ref b off)]
-                   [u    (sub b (+ off 2) (+ off 2 ulen))]
-                   [off2 (+ off 2 ulen)]
-                   [tlen (u16-ref b off2)]
-                   [t    (sub b (+ off2 2) (+ off2 2 tlen))]
-                   [off3 (+ off2 2 tlen)])
-              (loop (+ i 1) off3
-                    (cons (cons (utf8->string u) (utf8->string t)) acc)))))))
+        (if (= i n)
+            (if (= off blen)
+                (reverse acc)
+                (error 'deserialize-pairs "trailing payload bytes"))
+            (begin
+              (require-bytes 'deserialize-pairs b off 2)
+              (let* ([ulen (u16-ref b off)]
+                     [uoff (+ off 2)])
+                (require-bytes 'deserialize-pairs b uoff ulen)
+                (let* ([u    (sub b uoff (+ uoff ulen))]
+                       [off2 (+ uoff ulen)])
+                  (require-bytes 'deserialize-pairs b off2 2)
+                  (let* ([tlen (u16-ref b off2)]
+                         [toff (+ off2 2)])
+                    (require-bytes 'deserialize-pairs b toff tlen)
+                    (let* ([t    (sub b toff (+ toff tlen))]
+                           [off3 (+ toff tlen)])
+                      (loop (+ i 1) off3
+                            (cons (cons (utf8->string u) (utf8->string t)) acc)))))))))))
 
   ;; --- RSA-OAEP (SHA-256, empty label) ---
   ;; lHash is computed lazily: merely loading this module then needs no native