@@ -742,7 +742,16 @@ const createKeyExchange = (() => {
742
742
true
743
743
) ;
744
744
745
- packet [ p ] = MESSAGE . KEXDH_REPLY ;
745
+ switch ( this . type ) {
746
+ case 'group' :
747
+ packet [ p ] = MESSAGE . KEXDH_REPLY ;
748
+ break ;
749
+ case 'groupex' :
750
+ packet [ p ] = MESSAGE . KEXDH_GEX_REPLY ;
751
+ break ;
752
+ default :
753
+ packet [ p ] = MESSAGE . KEXECDH_REPLY ;
754
+ }
746
755
747
756
writeUInt32BE ( packet , serverPublicHostKey . length , ++ p ) ;
748
757
packet . set ( serverPublicHostKey , p += 4 ) ;
@@ -1359,7 +1368,7 @@ const createKeyExchange = (() => {
1359
1368
this . _public = this . _dh . generateKeys ( ) ;
1360
1369
}
1361
1370
}
1362
- setDHParams ( prime , generator ) {
1371
+ setDHParams ( prime , generator = Buffer . from ( [ 0x02 ] ) ) {
1363
1372
if ( ! Buffer . isBuffer ( prime ) )
1364
1373
throw new Error ( 'Invalid prime value' ) ;
1365
1374
if ( ! Buffer . isBuffer ( generator ) )
@@ -1380,6 +1389,8 @@ const createKeyExchange = (() => {
1380
1389
switch ( this . _step ) {
1381
1390
case 1 :
1382
1391
if ( this . _protocol . _server ) {
1392
+
1393
+ // Server
1383
1394
if ( type !== MESSAGE . KEXDH_GEX_REQUEST ) {
1384
1395
return doFatalError (
1385
1396
this . _protocol ,
@@ -1389,72 +1400,133 @@ const createKeyExchange = (() => {
1389
1400
DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1390
1401
) ;
1391
1402
}
1392
- // TODO: allow user implementation to provide safe prime and
1393
- // generator on demand to support group exchange on server side
1394
- return doFatalError (
1395
- this . _protocol ,
1396
- 'Group exchange not implemented for server' ,
1397
- 'handshake' ,
1398
- DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1403
+
1404
+ this . _protocol . _debug && this . _protocol . _debug (
1405
+ 'Received DH GEX Request'
1399
1406
) ;
1400
- }
1401
1407
1402
- if ( type !== MESSAGE . KEXDH_GEX_GROUP ) {
1403
- return doFatalError (
1404
- this . _protocol ,
1405
- `Received packet ${ type } instead of ${ MESSAGE . KEXDH_GEX_GROUP } ` ,
1406
- 'handshake' ,
1407
- DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1408
+ /*
1409
+ byte SSH_MSG_KEY_DH_GEX_REQUEST
1410
+ uint32 min, minimal size in bits of an acceptable group
1411
+ uint32 n, preferred size in bits of the group the server
1412
+ will send
1413
+ uint32 max, maximal size in bits of an acceptable group
1414
+ */
1415
+ bufferParser . init ( payload , 1 ) ;
1416
+ let minBits ;
1417
+ let prefBits ;
1418
+ let maxBits ;
1419
+ if ( ( minBits = bufferParser . readUInt32BE ( ) ) === undefined
1420
+ || ( prefBits = bufferParser . readUInt32BE ( ) ) === undefined
1421
+ || ( maxBits = bufferParser . readUInt32BE ( ) ) === undefined ) {
1422
+ bufferParser . clear ( ) ;
1423
+ return doFatalError (
1424
+ this . _protocol ,
1425
+ 'Received malformed KEXDH_GEX_REQUEST' ,
1426
+ 'handshake' ,
1427
+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1428
+ ) ;
1429
+ }
1430
+ bufferParser . clear ( ) ;
1431
+
1432
+ const primeGenerator =
1433
+ this . _protocol . _getDHParams ( minBits , prefBits , maxBits ) ;
1434
+ if ( ! Array . isArray ( primeGenerator ) ) {
1435
+ return doFatalError (
1436
+ this . _protocol ,
1437
+ 'No matching prime for KEXDH_GEX_REQUEST' ,
1438
+ 'handshake' ,
1439
+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1440
+ ) ;
1441
+ }
1442
+
1443
+ this . _minBits = minBits ;
1444
+ this . _prefBits = prefBits ;
1445
+ this . _maxBits = maxBits ;
1446
+
1447
+ this . setDHParams ( ...primeGenerator ) ;
1448
+ this . generateKeys ( ) ;
1449
+ const dh = this . getDHParams ( ) ;
1450
+
1451
+ this . _protocol . _debug && this . _protocol . _debug (
1452
+ 'Outbound: Sending KEXDH_GEX_GROUP'
1408
1453
) ;
1409
- }
1410
1454
1411
- this . _protocol . _debug && this . _protocol . _debug (
1412
- 'Received DH GEX Group'
1413
- ) ;
1455
+ let p = this . _protocol . _packetRW . write . allocStartKEX ;
1456
+ const packet =
1457
+ this . _protocol . _packetRW . write . alloc (
1458
+ 1 + 4 + dh . prime . length + 4 + dh . generator . length , true ) ;
1459
+ packet [ p ] = MESSAGE . KEXDH_GEX_GROUP ;
1460
+ writeUInt32BE ( packet , dh . prime . length , ++ p ) ;
1461
+ packet . set ( dh . prime , p += 4 ) ;
1462
+ writeUInt32BE ( packet , dh . generator . length ,
1463
+ p += dh . prime . length ) ;
1464
+ packet . set ( dh . generator , p += 4 ) ;
1465
+ this . _protocol . _cipher . encrypt (
1466
+ this . _protocol . _packetRW . write . finalize ( packet , true )
1467
+ ) ;
1414
1468
1415
- /*
1416
- byte SSH_MSG_KEX_DH_GEX_GROUP
1417
- mpint p, safe prime
1418
- mpint g, generator for subgroup in GF(p)
1419
- */
1420
- bufferParser . init ( payload , 1 ) ;
1421
- let prime ;
1422
- let gen ;
1423
- if ( ( prime = bufferParser . readString ( ) ) === undefined
1424
- || ( gen = bufferParser . readString ( ) ) === undefined ) {
1425
- bufferParser . clear ( ) ;
1426
- return doFatalError (
1427
- this . _protocol ,
1428
- 'Received malformed KEXDH_GEX_GROUP' ,
1429
- 'handshake' ,
1430
- DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1469
+ } else {
1470
+
1471
+ // Client
1472
+ if ( type !== MESSAGE . KEXDH_GEX_GROUP ) {
1473
+ return doFatalError (
1474
+ this . _protocol ,
1475
+ `Received packet ${ type } instead of ${ MESSAGE . KEXDH_GEX_GROUP } ` ,
1476
+ 'handshake' ,
1477
+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1478
+ ) ;
1479
+ }
1480
+
1481
+ this . _protocol . _debug && this . _protocol . _debug (
1482
+ 'Received DH GEX Group'
1431
1483
) ;
1432
- }
1433
- bufferParser . clear ( ) ;
1434
1484
1435
- // TODO: validate prime
1436
- this . setDHParams ( prime , gen ) ;
1437
- this . generateKeys ( ) ;
1438
- const pubkey = this . getPublicKey ( ) ;
1485
+ /*
1486
+ byte SSH_MSG_KEX_DH_GEX_GROUP
1487
+ mpint p, safe prime
1488
+ mpint g, generator for subgroup in GF(p)
1489
+ */
1490
+ bufferParser . init ( payload , 1 ) ;
1491
+ let prime ;
1492
+ let gen ;
1493
+ if ( ( prime = bufferParser . readString ( ) ) === undefined
1494
+ || ( gen = bufferParser . readString ( ) ) === undefined ) {
1495
+ bufferParser . clear ( ) ;
1496
+ return doFatalError (
1497
+ this . _protocol ,
1498
+ 'Received malformed KEXDH_GEX_GROUP' ,
1499
+ 'handshake' ,
1500
+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1501
+ ) ;
1502
+ }
1503
+ bufferParser . clear ( ) ;
1439
1504
1440
- this . _protocol . _debug && this . _protocol . _debug (
1441
- 'Outbound: Sending KEXDH_GEX_INIT'
1442
- ) ;
1505
+ // TODO: validate prime
1506
+ this . setDHParams ( prime , gen ) ;
1507
+ this . generateKeys ( ) ;
1508
+ const pubkey = this . getPublicKey ( ) ;
1443
1509
1444
- let p = this . _protocol . _packetRW . write . allocStartKEX ;
1445
- const packet =
1446
- this . _protocol . _packetRW . write . alloc ( 1 + 4 + pubkey . length , true ) ;
1447
- packet [ p ] = MESSAGE . KEXDH_GEX_INIT ;
1448
- writeUInt32BE ( packet , pubkey . length , ++ p ) ;
1449
- packet . set ( pubkey , p += 4 ) ;
1450
- this . _protocol . _cipher . encrypt (
1451
- this . _protocol . _packetRW . write . finalize ( packet , true )
1452
- ) ;
1510
+ this . _protocol . _debug && this . _protocol . _debug (
1511
+ 'Outbound: Sending KEXDH_GEX_INIT'
1512
+ ) ;
1453
1513
1514
+ let p = this . _protocol . _packetRW . write . allocStartKEX ;
1515
+ const packet =
1516
+ this . _protocol . _packetRW . write . alloc ( 1 + 4 + pubkey . length , true ) ;
1517
+ packet [ p ] = MESSAGE . KEXDH_GEX_INIT ;
1518
+ writeUInt32BE ( packet , pubkey . length , ++ p ) ;
1519
+ packet . set ( pubkey , p += 4 ) ;
1520
+ this . _protocol . _cipher . encrypt (
1521
+ this . _protocol . _packetRW . write . finalize ( packet , true )
1522
+ ) ;
1523
+ }
1454
1524
++ this . _step ;
1455
1525
break ;
1456
1526
case 2 :
1457
1527
if ( this . _protocol . _server ) {
1528
+
1529
+ // Server
1458
1530
if ( type !== MESSAGE . KEXDH_GEX_INIT ) {
1459
1531
return doFatalError (
1460
1532
this . _protocol ,
@@ -1463,30 +1535,90 @@ const createKeyExchange = (() => {
1463
1535
DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1464
1536
) ;
1465
1537
}
1538
+
1466
1539
this . _protocol . _debug && this . _protocol . _debug (
1467
1540
'Received DH GEX Init'
1468
1541
) ;
1469
- return doFatalError (
1470
- this . _protocol ,
1471
- 'Group exchange not implemented for server' ,
1472
- 'handshake' ,
1473
- DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1542
+
1543
+ /*
1544
+ byte SSH_MSG_KEX_DH_GEX_INIT
1545
+ mpint e
1546
+ */
1547
+ bufferParser . init ( payload , 1 ) ;
1548
+ let dhData ;
1549
+ if ( ( dhData = bufferParser . readString ( ) ) === undefined ) {
1550
+ bufferParser . clear ( ) ;
1551
+ return doFatalError (
1552
+ this . _protocol ,
1553
+ 'Received malformed KEXDH_GEX_INIT' ,
1554
+ 'handshake' ,
1555
+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1556
+ ) ;
1557
+ }
1558
+ bufferParser . clear ( ) ;
1559
+
1560
+ this . _dhData = dhData ;
1561
+
1562
+ let hostKey =
1563
+ this . _protocol . _hostKeys [ this . negotiated . serverHostKey ] ;
1564
+ if ( Array . isArray ( hostKey ) )
1565
+ hostKey = hostKey [ 0 ] ;
1566
+ this . _hostKey = hostKey ;
1567
+
1568
+ this . finish ( ) ;
1569
+
1570
+ } else {
1571
+
1572
+ // Client
1573
+ if ( type !== MESSAGE . KEXDH_GEX_REPLY ) {
1574
+ return doFatalError (
1575
+ this . _protocol ,
1576
+ `Received packet ${ type } instead of ${ MESSAGE . KEXDH_GEX_REPLY } ` ,
1577
+ 'handshake' ,
1578
+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1579
+ ) ;
1580
+ }
1581
+
1582
+ this . _protocol . _debug && this . _protocol . _debug (
1583
+ 'Received DH GEX Reply'
1474
1584
) ;
1475
- } else if ( type !== MESSAGE . KEXDH_GEX_REPLY ) {
1585
+ this . _step = 1 ;
1586
+ payload [ 0 ] = MESSAGE . KEXDH_REPLY ;
1587
+ this . parse = KeyExchange . prototype . parse ;
1588
+ this . parse ( payload ) ;
1589
+ }
1590
+
1591
+ ++ this . _step ;
1592
+ break ;
1593
+
1594
+ case 3 :
1595
+
1596
+ if ( type !== MESSAGE . NEWKEYS ) {
1476
1597
return doFatalError (
1477
1598
this . _protocol ,
1478
- `Received packet ${ type } instead of ${ MESSAGE . KEXDH_GEX_REPLY } ` ,
1599
+ `Received packet ${ type } instead of ${ MESSAGE . NEWKEYS } ` ,
1479
1600
'handshake' ,
1480
1601
DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1481
1602
) ;
1482
1603
}
1483
1604
this . _protocol . _debug && this . _protocol . _debug (
1484
- 'Received DH GEX Reply'
1605
+ 'Inbound: NEWKEYS'
1606
+ ) ;
1607
+ this . _receivedNEWKEYS = true ;
1608
+ ++ this . _step ;
1609
+ if ( this . _protocol . _server || this . _hostVerified )
1610
+ return this . finish ( ) ;
1611
+
1612
+ // Signal to current decipher that we need to change to a new decipher
1613
+ // for the next packet
1614
+ return false ;
1615
+ default :
1616
+ return doFatalError (
1617
+ this . _protocol ,
1618
+ `Received unexpected packet ${ type } after NEWKEYS` ,
1619
+ 'handshake' ,
1620
+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1485
1621
) ;
1486
- this . _step = 1 ;
1487
- payload [ 0 ] = MESSAGE . KEXDH_REPLY ;
1488
- this . parse = KeyExchange . prototype . parse ;
1489
- this . parse ( payload ) ;
1490
1622
}
1491
1623
}
1492
1624
}
0 commit comments