@@ -113,6 +113,10 @@ var cipherModes = map[string]*streamCipherMode{
113
113
// special case. If we add any more non-stream ciphers, we
114
114
// should invest a cleaner way to do this.
115
115
gcmCipherID : {16 , 12 , 0 , nil },
116
+
117
+ // insecure cipher, see http://www.isg.rhul.ac.uk/~kp/SandPfinal.pdf
118
+ // uncomment below to enable it.
119
+ // aes128cbcID: {16, aes.BlockSize, 0, nil},
116
120
}
117
121
118
122
// prefixLen is the length of the packet prefix that contains the packet length
@@ -342,3 +346,178 @@ func (c *gcmCipher) readPacket(seqNum uint32, r io.Reader) ([]byte, error) {
342
346
plain = plain [1 : length - uint32 (padding )]
343
347
return plain , nil
344
348
}
349
+
350
+ // cbcCipher implements aes128-cbc cipher defined in RFC 4253 section 6.1
351
+ type cbcCipher struct {
352
+ mac hash.Hash
353
+ decrypter cipher.BlockMode
354
+ encrypter cipher.BlockMode
355
+
356
+ // The following members are to avoid per-packet allocations.
357
+ seqNumBytes [4 ]byte
358
+ packetData []byte
359
+ macResult []byte
360
+ }
361
+
362
+ func newAESCBCCipher (iv , key , macKey []byte , algs directionAlgorithms ) (packetCipher , error ) {
363
+ c , err := aes .NewCipher (key )
364
+ if err != nil {
365
+ return nil , err
366
+ }
367
+ return & cbcCipher {
368
+ mac : macModes [algs .MAC ].new (macKey ),
369
+ decrypter : cipher .NewCBCDecrypter (c , iv ),
370
+ encrypter : cipher .NewCBCEncrypter (c , iv ),
371
+ packetData : make ([]byte , 1024 ),
372
+ }, nil
373
+ }
374
+
375
+ func maxUInt32 (a , b int ) uint32 {
376
+ if a > b {
377
+ return uint32 (a )
378
+ }
379
+ return uint32 (b )
380
+ }
381
+
382
+ const (
383
+ cbcMinPacketSizeMultiple = 8
384
+ cbcMinPacketSize = 16
385
+ cbcMinPaddingSize = 4
386
+ )
387
+
388
+ func (c * cbcCipher ) readPacket (seqNum uint32 , r io.Reader ) ([]byte , error ) {
389
+ blockSize := c .decrypter .BlockSize ()
390
+
391
+ // Read the header, which will include some of the subsequent data in the
392
+ // case of block ciphers - this is copied back to the payload later.
393
+ // How many bytes of payload/padding will be read with this first read.
394
+ firstBlockLength := (prefixLen + blockSize - 1 ) / blockSize * blockSize
395
+ firstBlock := c .packetData [:firstBlockLength ]
396
+ if _ , err := io .ReadFull (r , firstBlock ); err != nil {
397
+ return nil , err
398
+ }
399
+
400
+ c .decrypter .CryptBlocks (firstBlock , firstBlock )
401
+ length := binary .BigEndian .Uint32 (firstBlock [:4 ])
402
+ if length > maxPacket {
403
+ return nil , errors .New ("ssh: packet too large" )
404
+ }
405
+ if length + 4 < maxUInt32 (cbcMinPacketSize , blockSize ) {
406
+ // The minimum size of a packet is 16 (or the cipher block size, whichever
407
+ // is larger) bytes.
408
+ return nil , errors .New ("ssh: packet too small" )
409
+ }
410
+ // The length of the packet (including the length field but not the MAC) must
411
+ // be a multiple of the block size or 8, whichever is larger.
412
+ if (length + 4 )% maxUInt32 (cbcMinPacketSizeMultiple , blockSize ) != 0 {
413
+ return nil , errors .New ("ssh: invalid packet length multiple" )
414
+ }
415
+
416
+ paddingLength := uint32 (firstBlock [4 ])
417
+ if paddingLength < cbcMinPaddingSize || length <= paddingLength + 1 {
418
+ return nil , errors .New ("ssh: invalid packet length" )
419
+ }
420
+
421
+ var macSize uint32
422
+ if c .mac != nil {
423
+ macSize = uint32 (c .mac .Size ())
424
+ }
425
+
426
+ // Positions within the c.packetData buffer:
427
+ macStart := 4 + length
428
+ paddingStart := macStart - paddingLength
429
+
430
+ // Entire packet size, starting before length, ending at end of mac.
431
+ entirePacketSize := macStart + macSize
432
+
433
+ // Ensure c.packetData is large enough for the entire packet data.
434
+ if uint32 (cap (c .packetData )) < entirePacketSize {
435
+ // Still need to upsize and copy, but this should be rare at runtime, only
436
+ // on upsizing the packetData buffer.
437
+ c .packetData = make ([]byte , entirePacketSize )
438
+ copy (c .packetData , firstBlock )
439
+ } else {
440
+ c .packetData = c .packetData [:entirePacketSize ]
441
+ }
442
+
443
+ if _ , err := io .ReadFull (r , c .packetData [firstBlockLength :]); err != nil {
444
+ return nil , err
445
+ }
446
+
447
+ remainingCrypted := c .packetData [firstBlockLength :macStart ]
448
+ c .decrypter .CryptBlocks (remainingCrypted , remainingCrypted )
449
+
450
+ mac := c .packetData [macStart :]
451
+ if c .mac != nil {
452
+ c .mac .Reset ()
453
+ binary .BigEndian .PutUint32 (c .seqNumBytes [:], seqNum )
454
+ c .mac .Write (c .seqNumBytes [:])
455
+ c .mac .Write (c .packetData [:macStart ])
456
+ c .macResult = c .mac .Sum (c .macResult [:0 ])
457
+ if subtle .ConstantTimeCompare (c .macResult , mac ) != 1 {
458
+ return nil , errors .New ("ssh: MAC failure" )
459
+ }
460
+ }
461
+
462
+ return c .packetData [prefixLen :paddingStart ], nil
463
+ }
464
+
465
+ func (c * cbcCipher ) writePacket (seqNum uint32 , w io.Writer , rand io.Reader , packet []byte ) error {
466
+ effectiveBlockSize := maxUInt32 (cbcMinPacketSizeMultiple , c .encrypter .BlockSize ())
467
+
468
+ // Length of encrypted portion of the packet (header, payload, padding).
469
+ // Enforce minimum padding and packet size.
470
+ encLength := maxUInt32 (prefixLen + len (packet )+ cbcMinPaddingSize , cbcMinPaddingSize )
471
+ // Enforce block size.
472
+ encLength = (encLength + effectiveBlockSize - 1 ) / effectiveBlockSize * effectiveBlockSize
473
+
474
+ length := encLength - 4
475
+ paddingLength := int (length ) - (1 + len (packet ))
476
+
477
+ var macSize uint32
478
+ if c .mac != nil {
479
+ macSize = uint32 (c .mac .Size ())
480
+ }
481
+ // Overall buffer contains: header, payload, padding, mac.
482
+ // Space for the MAC is reserved in the capacity but not the slice length.
483
+ bufferSize := encLength + macSize
484
+ if uint32 (cap (c .packetData )) < bufferSize {
485
+ c .packetData = make ([]byte , encLength , bufferSize )
486
+ } else {
487
+ c .packetData = c .packetData [:encLength ]
488
+ }
489
+
490
+ p := c .packetData
491
+
492
+ // Packet header.
493
+ binary .BigEndian .PutUint32 (p , length )
494
+ p = p [4 :]
495
+ p [0 ] = byte (paddingLength )
496
+
497
+ // Payload.
498
+ p = p [1 :]
499
+ copy (p , packet )
500
+
501
+ // Padding.
502
+ p = p [len (packet ):]
503
+ if _ , err := io .ReadFull (rand , p ); err != nil {
504
+ return err
505
+ }
506
+
507
+ if c .mac != nil {
508
+ c .mac .Reset ()
509
+ binary .BigEndian .PutUint32 (c .seqNumBytes [:], seqNum )
510
+ c .mac .Write (c .seqNumBytes [:])
511
+ c .mac .Write (c .packetData )
512
+ // The MAC is now appended into the capacity reserved for it earlier.
513
+ c .packetData = c .mac .Sum (c .packetData )
514
+ }
515
+
516
+ c .encrypter .CryptBlocks (c .packetData [:encLength ], c .packetData [:encLength ])
517
+
518
+ if _ , err := w .Write (c .packetData ); err != nil {
519
+ return err
520
+ }
521
+
522
+ return nil
523
+ }
0 commit comments