This file is indexed.

/usr/share/racket/collects/net/win32-ssl.rkt is in racket-common 6.3-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
#lang racket/base
(require ffi/unsafe
         ffi/unsafe/define
         ffi/winapi
         ffi/unsafe/atomic
         ffi/unsafe/alloc
         racket/tcp
         racket/port)

;; A native Win32 implementation of SSL ports, which can be useful if
;; the openssl library is not available (perhaps because the openssl
;; library is going to be downloaded and installed via HTTPS). Various
;; options, including certificate checking, are not currently supported.

(provide win32-ssl-connect
         win32-ssl-abandon-port
         ports->win32-ssl-ports
         win32-ssl-port?
         win32-ssl-available?)

(define (win32-ssl-connect host port [protocol'sslv2-or-v3])
  (define-values (i o) (tcp-connect host port))
  (ports->win32-ssl-ports i o #:encrypt protocol))

(define (win32-ssl-abandon-port port)
  ;; We don't try to implement shutdown, anyway
  (if (input-port? port)
      (close-input-port port)
      (close-output-port port)))

;; ----------------------------------------
;; Win32 bindings

(define secur32-lib (and (eq? 'windows (system-type))
                         (ffi-lib "secur32.dll")))

(define win32-ssl-available? (and secur32-lib #t))

(define-ffi-definer define-secur32 secur32-lib
  #:default-make-fail make-not-available)

(define _LONG _long)
(define _ULONG _ulong)
(define _DWORD _int32)

(define-cstruct _cred-handle ([a _intptr] [b _intptr]))
(define-cstruct _ctx-handle ([a _intptr] [b _intptr]))

(define _SECURITY_STATUS _ULONG)
(define _TimeStamp _int64)

(define SECPKG_CRED_INBOUND #x00000001)
(define SECPKG_CRED_OUTBOUND #x00000002)

(define ISC_REQ_REPLAY_DETECT #x00000004)
(define ISC_REQ_SEQUENCE_DETECT #x00000008)
(define ISC_REQ_CONFIDENTIALITY #x00000010)
(define ISC_REQ_ALLOCATE_MEMORY #x00000100)
(define ISC_REQ_STREAM #x00008000)
(define ISC_REQ_USE_SUPPLIED_CREDS #x00000080)
(define ISC_REQ_MANUAL_CRED_VALIDATION #x00080000)

(define SECURITY_NATIVE_DREP #x00000010)

(define SECBUFFER_VERSION 0)
(define SECBUFFER_EMPTY 0)
(define SECBUFFER_DATA 1)
(define SECBUFFER_TOKEN 2)
(define SECBUFFER_EXTRA 5)
(define SECBUFFER_STREAM_TRAILER 6)
(define SECBUFFER_STREAM_HEADER 7)
(define SECBUFFER_ALERT 17)

(define SEC_E_OK 0)
(define SEC_I_CONTINUE_NEEDED #x00090312)
(define SEC_I_CONTEXT_EXPIRED #x00090317)
(define SEC_E_INCOMPLETE_MESSAGE #x80090318)
(define SEC_E_BUFFER_TOO_SMALL #x80090321)

(define SECPKG_ATTR_STREAM_SIZES 4)

(define-cstruct _SecBuffer ([cbBuffer _ULONG]
                            [BufferType _ULONG]
                            [pvBuffer _pointer]))

(define-cstruct _SecBufferDesc ([vers _ULONG]
                                [cBuffers _ULONG]
                                [pBuffers _pointer])) ; array of _SecBuffers

(define-cstruct _SCHANNEL_CRED ([version _DWORD]
                                [cCreds _DWORD]
                                [paCred _pointer]
                                [hRootStore _pointer]
                                [cMappers _DWORD]
                                [aphMappers _pointer]
                                [cSupportedAlgs _DWORD]
                                [palgSupportedAlgs _pointer]
                                [grbitEnabledProtocols _DWORD]
                                [dwMinimumCipherStrength _DWORD]
                                [dwMaximumCipherStrength _DWORD]
                                [dwSessionLifespan _DWORD]
                                [dwFlags _DWORD]
                                [dwCredFormat _DWORD]))

(define-cstruct _SecPkgContext_StreamSizes ([cbHeader _ULONG]
                                            [cbTrailer _ULONG]
                                            [cbMaximumMessage _ULONG]
                                            [cBuffers _ULONG]
                                            [cbBlockSize _ULONG]))

(define SP_PROT_SSL2_SERVER #x00000004)
(define SP_PROT_SSL2_CLIENT #x00000008)
(define SP_PROT_SSL2 (bitwise-ior SP_PROT_SSL2_SERVER SP_PROT_SSL2_CLIENT))
(define SP_PROT_SSL3_SERVER #x00000010)
(define SP_PROT_SSL3_CLIENT #x00000020)
(define SP_PROT_SSL3 (bitwise-ior SP_PROT_SSL3_SERVER SP_PROT_SSL3_CLIENT))
(define SP_PROT_TLS1_SERVER #x00000040)
(define SP_PROT_TLS1_CLIENT #x00000080)
(define SP_PROT_TLS1 (bitwise-ior SP_PROT_TLS1_SERVER SP_PROT_TLS1_CLIENT))
(define SCH_CRED_MANUAL_CRED_VALIDATION #x00000008)
(define SCH_CRED_NO_DEFAULT_CREDS #x00000010)
(define SCHANNEL_CRED_VERSION #x00000004)

(define-secur32 InitSecurityInterfaceW
  (_fun #:abi winapi -> _pointer))

(define (check-status who r)
  (unless (zero? r)
    (error who "failed: ~x" r)))

(define-secur32 AcquireCredentialsHandleW
  (_fun #:abi winapi
        _string/utf-16 ; principal
        _string/utf-16 ; package, such as "Negotiate"
        _ULONG ; SECPKG_CRED_INBOUND or SECPKG_CRED_OUTBOUND
        _pointer ; pvLogonID, NULL ok
        _pointer ; pAuthData, NULL ok
        _pointer ; pGetKeyFn, NULL ok
        _pointer ; pvGetKeyArgument, NULL ok
        _cred-handle-pointer ; receives the result
        (ts : (_ptr o _TimeStamp))
        ->
        (r : _SECURITY_STATUS)
        ->
        (check-status 'AcquireCredentialsHandleW r)))

(define-secur32 FreeCredentialsHandle
  (_fun #:abi winapi
        _cred-handle-pointer
        ->
        (r : _SECURITY_STATUS)
        ->
        (check-status 'FreeCredentialsHandle r)))

(define-secur32 FreeContextBuffer
  (_fun #:abi winapi
        _pointer
        ->
        (r : _SECURITY_STATUS)
        ->
        (check-status  'FreeContextBuffer r)))

(define-secur32 InitializeSecurityContextW
  (_fun #:abi winapi
        _cred-handle-pointer
        _ctx-handle-pointer/null ; NULL on first call
        _string/utf-16 ; server name
        _ULONG ; ISC_REQ_ALLOCATE_MEMORY, etc.
        _ULONG ; reserved, 0
        _ULONG ; SECURITY_NATIVE_DREP
        _SecBufferDesc-pointer/null ; input, NULL on first call
        _ULONG ; reserved, 0
        _ctx-handle-pointer/null ; non-NULL on first call only
        _SecBufferDesc-pointer ; output buffer
        (attr : (_ptr o _ULONG))
        (ts : (_ptr o _TimeStamp)) ; timeout out, can ignore
        ->
        (r : _SECURITY_STATUS)
        ->
        (values r attr)))

(define-secur32 DeleteSecurityContext
  (_fun #:abi winapi
        _ctx-handle-pointer
        ->
        (r : _SECURITY_STATUS)
        ->
        (check-status 'DeleteSecurityContext r)))

(define-secur32 DecryptMessage
  (_fun #:abi winapi
        _ctx-handle-pointer
        _SecBufferDesc-pointer ; input and output buffer
        _ULONG
        _pointer
        ->
        _SECURITY_STATUS))

(define-secur32 EncryptMessage
  (_fun #:abi winapi
        _ctx-handle-pointer
        _ULONG
        _SecBufferDesc-pointer ; input and output buffer
        _ULONG
        ->
        _SECURITY_STATUS))

(define-secur32 QueryContextAttributesW
  (_fun #:abi winapi
        _ctx-handle-pointer
        _ULONG  ; attribute
        _pointer ; receives the result
        ->
        (r : _SECURITY_STATUS)
        ->
        (check-status 'QueryContextAttributes r)))

(define-logger win32-ssl)

;; ----------------------------------------
;; Credential and context finalization

;; We allocate a credential and context handle at the same time
;; (atomically), so we only have to finalize credential--context
;; pairs.

(define free-ctx
  ((deallocator)
   (lambda (ctx)
     (unless (and (zero? (ctx-handle-a (car ctx)))
                  (zero? (ctx-handle-b (car ctx))))
       (DeleteSecurityContext (car ctx)))
     (FreeCredentialsHandle (cdr ctx)))))
(define make-ctx
  ((allocator free-ctx)
   (lambda (cred)
     (cons (make-ctx-handle 0 0) cred))))
(define (ctx->handle ctx) (car ctx))

;; ----------------------------------------
;; Helpers to manage the clunky SecBuffer API

(define (make-SecBuffers n)
  (cast (malloc n _SecBuffer 'atomic-interior) _pointer _SecBuffer-pointer))

(define (make-SecBuffers! sbs . vals)
  (define n
    (let loop ([pos 0] [vals vals])
      (cond
       [(null? vals) pos]
       [else
        (define sb (ptr-ref sbs _SecBuffer pos))
        (set-SecBuffer-cbBuffer! sb (car vals))
        (set-SecBuffer-BufferType! sb (cadr vals))
        (set-SecBuffer-pvBuffer! sb (caddr vals))
        (loop (add1 pos) (cdddr vals))])))
  (make-SecBufferDesc SECBUFFER_VERSION
                      n
                      sbs))

;; ----------------------------------------
;; Creating a context (i.e., an SSL connection)

;; Returns a context plus initial bytes for stream
(define (create-context protocol i o out-sb in-sb)
  ;; Pointers to particular SecBuffer records:
  (define out-sb0 (ptr-ref out-sb _SecBuffer 0))
  (define in-sb0 (ptr-ref in-sb _SecBuffer 0))
  (define in-sb1 (ptr-ref in-sb _SecBuffer 1))

  ;; To stream communication during protocol set-up:
  (define buffer-size 4096)
  (define buffer (make-sized-byte-string (malloc buffer-size 'atomic-interior)
                                         buffer-size))

  (call-as-atomic
   (lambda ()
     ;; Allocate credentials.
     (define cred (make-cred-handle 0 0))
     (AcquireCredentialsHandleW #f
                                   "Microsoft Unified Security Protocol Provider"
                                   SECPKG_CRED_OUTBOUND ; SECPKG_CRED_INBOUND or SECPKG_CRED_OUTBOUND
                                   #f
                                   (make-SCHANNEL_CRED SCHANNEL_CRED_VERSION
                                                       0 #f
                                                       #f
                                                       0 #f ; mappers
                                                       0 #f ; algs
                                                       (case protocol
                                                         [(auto sslv2-or-v3)
                                                          (bitwise-ior SP_PROT_TLS1)]
                                                         [(sslv2) SP_PROT_SSL2]
                                                         [(sslv3) SP_PROT_SSL3]
                                                         [(tls) SP_PROT_TLS1])
                                                       0 0 0
                                                       (bitwise-ior SCH_CRED_MANUAL_CRED_VALIDATION)
                                                       0)
                                   #f
                                   #f
                                   cred)

     ;; Allocate a content and take responsibility for freeing
     ;; credientials, but it's not a real content until the
     ;; 0 values are replaced with an new context:
     (define ctx (make-ctx cred))

     ;; Loop to let the client and server communicate to set up the protocol:
     (let loop ([data-len 0] [init? #t])
       (define-values (r attr)
         (InitializeSecurityContextW cred
                                     (if init? #f (ctx->handle ctx))
                                     #f
                                     (bitwise-ior ISC_REQ_REPLAY_DETECT ISC_REQ_SEQUENCE_DETECT
                                                  ISC_REQ_CONFIDENTIALITY ISC_REQ_STREAM 
                                                  ISC_REQ_ALLOCATE_MEMORY
                                                  ISC_REQ_MANUAL_CRED_VALIDATION)
                                     0
                                     SECURITY_NATIVE_DREP
                                     (if init?
                                         #f
                                         (make-SecBuffers! in-sb
                                                           data-len
                                                           SECBUFFER_TOKEN
                                                           buffer
                                                           0
                                                           SECBUFFER_EMPTY
                                                           #f))
                                     0
                                     (if init? (ctx->handle ctx) #f)
                                     (make-SecBuffers! out-sb
                                                       0
                                                       SECBUFFER_TOKEN
                                                       #f)))
       (log-win32-ssl-debug "init context: status ~x" r)

       (when (or (= r SEC_E_OK)
                 (= r SEC_I_CONTINUE_NEEDED))
         (unless (zero? (SecBuffer-cbBuffer out-sb0))
           ;; Go back to non-atomic mode for a potentially blocking write:
           (call-as-nonatomic
            (lambda ()
              (log-win32-ssl-debug "init context: write ~a" (SecBuffer-cbBuffer out-sb0))
              (write-bytes (make-sized-byte-string (SecBuffer-pvBuffer out-sb0)
                                                   (SecBuffer-cbBuffer out-sb0))
                           o)
              (flush-output o)))
           (FreeContextBuffer (SecBuffer-pvBuffer out-sb0))))

       (define (get-leftover-bytes)
         (if (equal? (SecBuffer-BufferType in-sb1) SECBUFFER_EXTRA)
             ;; Same the leftover bytes:
             (let ([amt (SecBuffer-cbBuffer in-sb1)])
               (log-win32-ssl-debug "init context: leftover ~a" amt)
               (memcpy buffer (ptr-add buffer (- data-len amt)) amt)
               amt)
             0))

       (cond
        [(= r SEC_E_OK) 
         ;; Success:
         (log-win32-ssl-debug "init context: done")
         (values ctx
                 (let ([n (get-leftover-bytes)])
                   (subbytes buffer 0 n)))]
        [(= r SEC_I_CONTINUE_NEEDED)
         ;; Pull more data from the server
         (define data-len (get-leftover-bytes))
         ;; Unlikely, but maybe it's possible that we don't have room
         ;; to read more due to leftover bytes:
         (when (= data-len buffer-size)
           (define new-buffer (malloc (* 2 buffer-size) 'atomic-interior))
           (memcpy new-buffer buffer buffer-size)
           (set! buffer-size (* 2 buffer-size))
           (set! buffer (make-sized-byte-string new-buffer buffer-size)))
         ;; Go back to non-atomic mode for a potentially blocking read:
         (define n (call-as-nonatomic
                    (lambda ()
                      (read-bytes-avail! buffer i data-len buffer-size))))
         (log-win32-ssl-debug "init context: read ~a" n)
         (when (eof-object? n) (error "unexpected EOF"))
         (loop (+ data-len n) #f)]
        ;; Some other things are allowed to happen without implying
        ;; failure, but we don't handle all of them.
        [else (error 'create-context
                     "unexpected result: ~x" r)])))))

(define (decrypt ctx in-pre-r in-post-w out-sb)
  ;; Read encrypted byte from `in-pre-r', write decrypted bytes to
  ;; `in-port-w'.
  ;; Loop to try to get a big enough chunk from the input to be able
  ;; to decrypt it.
  (let loop ([size 4096] [prev-n 0])
    (define buffer (make-bytes size))
    (define n (peek-bytes-avail!* buffer 0 #f in-pre-r))
    (define r (DecryptMessage (ctx->handle ctx)
                              (make-SecBuffers! out-sb
                                                n
                                                SECBUFFER_DATA
                                                buffer
                                                0
                                                SECBUFFER_EMPTY
                                                #f
                                                0
                                                SECBUFFER_EMPTY
                                                #f
                                                0
                                                SECBUFFER_EMPTY
                                                #f)
                              0
                              #f))
    (log-win32-ssl-debug "decrypt status: ~x" r)
    (cond
     [(= r SEC_E_OK)
      ;; Successfully decrypted some. Figure out how many bytes
      ;; were used (to remove them from `in-pre-r') and
      ;; write decrypted bytes to `in-post-w'.
      (define sb
        (for/or ([i (in-range 0 4)])
          (define sb (ptr-ref out-sb _SecBuffer i))
          (and (= SECBUFFER_DATA (SecBuffer-BufferType sb))
               sb)))
      (unless sb
        (error "expected decrypted data"))
      (write-bytes (make-sized-byte-string (SecBuffer-pvBuffer sb)
                                           (SecBuffer-cbBuffer sb))
                   in-post-w)
      (define remain (or (for/or ([i (in-range 1 4)])
                           (define sb (ptr-ref out-sb _SecBuffer i))
                           (and (= SECBUFFER_EXTRA (SecBuffer-BufferType sb))
                                (SecBuffer-cbBuffer sb)))
                         0))
      (log-win32-ssl-debug "decrypted ~a to ~a (~a remain)" 
                           (- n remain)
                           (SecBuffer-cbBuffer sb)
                           remain)
      (read-bytes! buffer in-pre-r 0 (- n remain))
      (unless (zero? remain)
        (loop size 0))]
     [(= r SEC_E_INCOMPLETE_MESSAGE)
      ;; If `prev-n' is the same as `n', then we must have
      ;; tried everything that's currently available.
      (unless (= prev-n n)
        ;; Try with a larger buffer:
        (loop (* size 2) n))]
     [(= r SEC_I_CONTEXT_EXPIRED)
      ;; Other end closed the connection.
      (close-output-port in-post-w)]
     [else
      (error 'decrypt "unexpected result: ~x" r)])))

(define (encrypt ctx bstr start end out-sb sizes buffer)
  ;; Encrypt bytes [start, end) from bstr.
  ;; If we have too much to encrypt at once, we'll encrypt
  ;; halves separately:
  (define (divide-and-conquer)
    
    (define mid (quotient (+ start end) 2))
    (bytes-append (encrypt ctx bstr start mid sizes buffer)
                  (encrypt ctx bstr mid end sizes buffer)))
  (cond
   [((- end start) . > . (bytes-length buffer))
    ;; Too much right from the start:
    (divide-and-conquer)]
   [else
    ;; EncryptMessage expects certain size buffers in a
    ;; certain layout:
    (define msize (SecPkgContext_StreamSizes-cbMaximumMessage sizes))
    (define hsize (SecPkgContext_StreamSizes-cbHeader sizes))
    (define tsize (SecPkgContext_StreamSizes-cbTrailer sizes))
    (define dsize (- end start))
    (memcpy buffer hsize bstr start (- end start))
    (define r (EncryptMessage (ctx->handle ctx)
                              0
                              (make-SecBuffers! out-sb
                                                hsize
                                                SECBUFFER_STREAM_HEADER
                                                buffer
                                                dsize
                                                SECBUFFER_DATA
                                                (ptr-add buffer hsize)
                                                tsize
                                                SECBUFFER_STREAM_TRAILER
                                                (ptr-add buffer (+ hsize dsize))
                                                0
                                                SECBUFFER_EMPTY
                                                #f)
                              0))
    (log-win32-ssl-debug "encrypt status: ~x" r)
    (cond
     [(= r SEC_E_OK)
      ;; Success:
      (define len (+ (SecBuffer-cbBuffer (ptr-ref out-sb _SecBuffer 0))
                     (SecBuffer-cbBuffer (ptr-ref out-sb _SecBuffer 1))
                     (SecBuffer-cbBuffer (ptr-ref out-sb _SecBuffer 2))))
      (subbytes buffer 0 len)]
     [(= r SEC_E_BUFFER_TOO_SMALL)
      ;; The encrypted bytes don't fit in the unencrypted space?
      (divide-and-conquer)]
     [else
      (error 'decrypt "unexpected result: ~x" r)])]))

;; Wrap input and output ports to produce SSL versions of the ports:
(define (ports->win32-ssl-ports i o #:encrypt [protocol 'sslv2-or-v3])
  ;; Working space for encoding, decoding, and more:
  (define out-sb (make-SecBuffers 4))
  (define in-sb (make-SecBuffers 2))
  
  ;; Allocate the encoding/decoding context:
  (define-values (ctx init-bytes) (create-context protocol i o out-sb in-sb))

  ;; Get some sizes that we need for encoding:
  (define sizes (make-SecPkgContext_StreamSizes 0 0 0 0 0))
  (QueryContextAttributesW (ctx->handle ctx)
                           SECPKG_ATTR_STREAM_SIZES
                           sizes)
  (define msize (SecPkgContext_StreamSizes-cbMaximumMessage sizes))
  (define hsize (SecPkgContext_StreamSizes-cbHeader sizes))
  (define tsize (SecPkgContext_StreamSizes-cbTrailer sizes))

  ;; Some pipes to manage the decoding stream:
  (define-values (in-pre-r in-pre-w) (make-pipe))
  (define-values (in-post-r in-post-w) (make-pipe))

  (write-bytes init-bytes in-pre-w)
  (decrypt ctx in-pre-r in-post-w out-sb)

  ;; More working space:
  (define buffer (make-bytes (max 8000 (+ msize hsize tsize))))

  ;; Port lock and state:
  (define lock (make-semaphore 1))
  (define leftover-bytes #f)
  (define refcount 2)

  ;; Close original ports when both new ports are closed:
  (define (close!)
    (set! refcount (sub1 refcount))
    (when (zero? refcount)
      (close-input-port i)
      (close-output-port o)
      (let ([v ctx])
        (set! ctx #f)
        (when v (free-ctx v)))))

  ;; Callbacks used below (written here so that they're allocated once):
  (define (lock-unavailable/read) (wrap-evt lock (lambda () 0)))
  (define (lock-unavailable/write) (wrap-evt lock (lambda () #f)))

  (define (read-in bstr)
    (let loop ()
      (define n (read-bytes-avail!* bstr in-post-r))
      (cond
       [(eof-object? n) n]
       [(zero? n)
        ;; Any input on the underlying port?
        (define n (read-bytes-avail!* buffer i))
        (cond
         [(eof-object? n)
          ;; Nothing decrypted, hit eof; return eof, even though
          ;; we have leftover encrypted bytes:
          (close-output-port in-post-w)
          n]
         [(zero? n)
          ;; Nothing decrypted, no new input, so wait for input:
          (log-win32-ssl-debug "blocked")
          (wrap-evt i (lambda (v) 0))]
         [else
          (log-win32-ssl-debug "underlying receive: ~a" n)
          ;; Get some fresh bytes, so try decoding now:
          (write-bytes buffer in-pre-w 0 n)
          (decrypt ctx in-pre-r in-post-w out-sb)
          (loop)])]
       [else n])))

  ;; The new input port:
  (define in (make-input-port/read-to-peek
              (format "SSL ~a" (object-name i))
              ;; read:
              (lambda (bstr)
                (call-with-semaphore
                 lock
                 read-in
                 lock-unavailable/read
                 bstr))
              ;; peek:
              (lambda (bstr offset slow)
                ;; Try fast peek on decrypted port:
                (define n (peek-bytes-avail!* bstr offset #f in-post-r))
                (if (zero? n)
                    (slow bstr offset)
                    n))
              ;; close
              (lambda ()
                (call-with-semaphore
                 lock
                 close!))))

  
  (define (write-out bstr start end non-block? enable-break?)
    (cond
     [(and (= start end)
           (not leftover-bytes))
      ;; Nothing to flush:
      0]
     [(not leftover-bytes)
      ;; Nothing in the output buffer, so we can encrypt more
      (define encrypted-bstr (encrypt ctx bstr start end out-sb sizes buffer))
      (define n (write-bytes-avail* encrypted-bstr o))
      (cond
       [(zero? n)
        (wrap-evt o (lambda (v) #f))]
       [(= n (bytes-length encrypted-bstr))
        ;; all written
        (- end start)]
       [else
        ;; we're forced to save the leftover bytes and
        ;; claim that they're written anyway:
        (set! leftover-bytes (subbytes encrypted-bstr n))
        (- end start)])]
     [else
      ;; Try sending leftover bytes (for flush or not):
      (define n (write-bytes-avail* leftover-bytes o))
      (cond
       [(zero? n) 
        (wrap-evt o (lambda (v) #f))]
       [(= n (bytes-length leftover-bytes))
        (set! leftover-bytes #f)
        (if (= start end)
            0 ; flushed all
            #f)]
       [else
        (set! leftover-bytes (subbytes leftover-bytes n))
        #f])]))
  
  ;; The new output port:
  (define out (make-output-port
               (format "SSL ~a" (object-name 0))
               o
               ;; write-out
               (lambda (bstr start end non-block? enable-break?)
                 (call-with-semaphore
                  lock
                  write-out                  
                  lock-unavailable/write
                  bstr start end non-block? enable-break?))
               ;; close
               (lambda ()
                 ;; flush:
                 (let loop ()
                   (define r
                     (call-with-semaphore
                      lock
                      (lambda ()
                        (write-out #"" 0 0 #f #f))))
                   (cond
                    [(equal? r 0) (void)]
                    [(evt? r) (sync r) (loop)]
                    [else (loop)]))
                 ;; actually close:
                 (call-with-semaphore
                  lock
                  close!))))

  ;; Done:
  (values (register in) (register out)))

;; ----------------------------------------
;; Recognizing win32 ports

(define win32-ssl-ports (make-weak-hash))

(define (register p)
  (hash-set! win32-ssl-ports p #t)
  p)

(define (win32-ssl-port? p)
  (hash-ref win32-ssl-ports p #f))

;; ----------------------------------------
;; Initialization

(when (eq? 'windows (system-type))
  (void (InitSecurityInterfaceW)))

;; ----------------------------------------

#;
(module+ main
  ;; Use `openssl' to implement server side for tests:
  (require openssl)
  (define server (ssl-make-server-context))
  (ssl-load-certificate-chain! server (collection-file-path "test.pem" "openssl"))
  (ssl-load-private-key! server (collection-file-path "test.pem" "openssl"))

  ;; Check that data is sent correctly:
  (define N 100)
  (define M 3)
  (define s (make-bytes N))
  (for ([i N])
    (bytes-set! s i (bitwise-and i 255)))
  (for ([c 100])
    (printf "~s\n" c)
    (define-values (i1 o1) (make-pipe (+ 4096 (random 4096))))
    (define-values (i2 o2) (make-pipe (+ 4096 (random 4096))))
    (define (fail who) (log-error "no good ~s" who) (exit 1))
    (define t1
      (thread
       (lambda ()
         (define-values (si so) (ports->ssl-ports i1 o2
                                                  #:mode 'accept
                                                  #:context server))
         (for ([j M]) (write s so))
         (flush-output so)
         (for ([j M])
           (unless (equal? s (read si))
             (fail 'server)))
         (close-output-port so)
         (close-input-port si))))
    (define t2
     (thread
      (lambda ()
        (define-values (ci co) (ports->win32-ssl-ports i2 o1))
        (for ([j M])
          (unless (equal? s (read ci))
            (fail 'client)))
        (for ([j M])
          (write s co))
        (close-output-port co)
        (close-input-port ci))))
    (sync t1)
    (sync t2)))