typed-rust: bytevector-copy slice primitive + AES-GCM nonce lifetime fix

ober

87120f0c7184f9e4d4a680fe7222efa794e8fe0c

diff --git a/lib/jerboa/typed/checker.ss b/lib/jerboa/typed/checker.ss
index 54d507d..42ec470 100644
--- a/lib/jerboa/typed/checker.ss
+++ b/lib/jerboa/typed/checker.ss
@@ -376,6 +376,11 @@
       (cons 'bytevector-append
             (make-typed-call-sig (list 'Bytes 'Bytes) 'Bytes '()
               'bytevector-append '()))
+      ;; (bytevector-copy bv start end) -> the slice bv[start..end] as a fresh
+      ;; owned buffer (R7RS 3-arg form); splits framed buffers in byte parsers.
+      (cons 'bytevector-copy
+            (make-typed-call-sig (list 'Bytes 'Nat 'Nat) 'Bytes '()
+              'bytevector-copy '()))
       ;; an Int as its 8-byte little-endian i64 encoding (Rust i64::to_le_bytes).
       (cons 'integer->le-bytes
             (make-typed-call-sig (list 'Int) 'Bytes '()
diff --git a/lib/jerboa/typed/rust.ss b/lib/jerboa/typed/rust.ss
index a48fe4a..3e152b4 100644
--- a/lib/jerboa/typed/rust.ss
+++ b/lib/jerboa/typed/rust.ss
@@ -1435,8 +1435,11 @@
       "{ use aes_gcm::aead::{Aead, KeyInit, Payload}; "
       "let __c = aes_gcm::Aes256Gcm::new_from_slice(" (bytes-slice (car args))
       ").expect(\"AES-256-GCM key is 32 bytes\"); "
-      "let __n = aes_gcm::Nonce::from_slice(" (bytes-slice (cadr args))
-      "); __c.encrypt(__n, Payload { msg: " (bytes-slice (caddr args))
+      ;; bind the nonce to an owned local: Nonce::from_slice borrows it and the
+      ;; borrow must outlive the encrypt() call (a temporary would drop early).
+      "let __nonce = (" (emit-expression (cadr args)) "); "
+      "let __n = aes_gcm::Nonce::from_slice(&__nonce[..]); "
+      "__c.encrypt(__n, Payload { msg: " (bytes-slice (caddr args))
       ", aad: " (bytes-slice (cadddr args))
       " }).expect(\"AES-256-GCM encryption within size limits\") }"))
 
@@ -1451,8 +1454,10 @@
       "{ use aes_gcm::aead::{Aead, KeyInit, Payload}; "
       "let __c = aes_gcm::Aes256Gcm::new_from_slice(" (bytes-slice (car args))
       ").expect(\"AES-256-GCM key is 32 bytes\"); "
-      "let __n = aes_gcm::Nonce::from_slice(" (bytes-slice (cadr args))
-      "); match __c.decrypt(__n, Payload { msg: " (bytes-slice (caddr args))
+      ;; bind the nonce to an owned local so its borrow outlives decrypt().
+      "let __nonce = (" (emit-expression (cadr args)) "); "
+      "let __n = aes_gcm::Nonce::from_slice(&__nonce[..]); "
+      "match __c.decrypt(__n, Payload { msg: " (bytes-slice (caddr args))
       ", aad: " (bytes-slice (cadddr args))
       " }) { Ok(__pt) => Some(__pt), Err(_) => None } }"))
 
@@ -1479,6 +1484,17 @@
       (bytes-slice (cadr args))
       "); __v }"))
 
+  ;; (bytevector-copy bv start end) -> bv[start..end] as a fresh owned buffer.
+  ;; Nat indices widen to usize like bytevector-u8-ref; an out-of-range slice
+  ;; panics and is caught by the C-ABI wrapper (caller validates lengths).
+  (def (emit-bytevector-copy args)
+    (unless (= (length args) 3)
+      (error 'typed-rust "bytevector-copy expects bv, start and end operands" args))
+    (string-append
+      "(" (emit-expression (car args))
+      "[(" (emit-expression (cadr args)) ") as usize..("
+      (emit-expression (caddr args)) ") as usize].to_vec())"))
+
   ;; (integer->le-bytes n) -> the 8-byte little-endian i64 encoding, matching
   ;; Rust's i64::to_le_bytes (secmon serializes timestamps this way).
   (def (emit-integer->le-bytes args)
@@ -1724,6 +1740,7 @@
         [(string->utf8) (emit-string->utf8 args)]
         [(utf8->string) (emit-utf8->string args)]
         [(bytevector-append) (emit-bytevector-append args)]
+        [(bytevector-copy) (emit-bytevector-copy args)]
         [(integer->le-bytes) (emit-integer->le-bytes args)]
         [(make-bytevector) (emit-make-bytevector args)]
         [(exact->inexact) (emit-to-float args)]