@@ -771,7 +771,16 @@ const createKeyExchange = (() => {
771771 true
772772 ) ;
773773
774- packet [ p ] = MESSAGE . KEXDH_REPLY ;
774+ switch ( this . type ) {
775+ case 'group' :
776+ packet [ p ] = MESSAGE . KEXDH_REPLY ;
777+ break ;
778+ case 'groupex' :
779+ packet [ p ] = MESSAGE . KEXDH_GEX_REPLY ;
780+ break ;
781+ default :
782+ packet [ p ] = MESSAGE . KEXECDH_REPLY ;
783+ }
775784
776785 writeUInt32BE ( packet , serverPublicHostKey . length , ++ p ) ;
777786 packet . set ( serverPublicHostKey , p += 4 ) ;
@@ -1406,7 +1415,7 @@ const createKeyExchange = (() => {
14061415 this . _public = this . _dh . generateKeys ( ) ;
14071416 }
14081417 }
1409- setDHParams ( prime , generator ) {
1418+ setDHParams ( prime , generator = Buffer . from ( [ 0x02 ] ) ) {
14101419 if ( ! Buffer . isBuffer ( prime ) )
14111420 throw new Error ( 'Invalid prime value' ) ;
14121421 if ( ! Buffer . isBuffer ( generator ) )
@@ -1427,6 +1436,8 @@ const createKeyExchange = (() => {
14271436 switch ( this . _step ) {
14281437 case 1 : {
14291438 if ( this . _protocol . _server ) {
1439+
1440+ // Server
14301441 if ( type !== MESSAGE . KEXDH_GEX_REQUEST ) {
14311442 return doFatalError (
14321443 this . _protocol ,
@@ -1436,73 +1447,143 @@ const createKeyExchange = (() => {
14361447 DISCONNECT_REASON . KEY_EXCHANGE_FAILED
14371448 ) ;
14381449 }
1439- // TODO: allow user implementation to provide safe prime and
1440- // generator on demand to support group exchange on server side
1441- return doFatalError (
1442- this . _protocol ,
1443- 'Group exchange not implemented for server' ,
1444- 'handshake' ,
1445- DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1450+
1451+ this . _protocol . _debug && this . _protocol . _debug (
1452+ 'Received DH GEX Request'
14461453 ) ;
1447- }
14481454
1449- if ( type !== MESSAGE . KEXDH_GEX_GROUP ) {
1450- return doFatalError (
1451- this . _protocol ,
1452- `Received packet ${ type } instead of ${ MESSAGE . KEXDH_GEX_GROUP } ` ,
1453- 'handshake' ,
1454- DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1455+ /*
1456+ byte SSH_MSG_KEY_DH_GEX_REQUEST
1457+ uint32 min, minimal size in bits of an acceptable group
1458+ uint32 n, preferred size in bits of the group the server
1459+ will send
1460+ uint32 max, maximal size in bits of an acceptable group
1461+ */
1462+ bufferParser . init ( payload , 1 ) ;
1463+ let minBits ;
1464+ let prefBits ;
1465+ let maxBits ;
1466+ if ( ( minBits = bufferParser . readUInt32BE ( ) ) === undefined
1467+ || ( prefBits = bufferParser . readUInt32BE ( ) ) === undefined
1468+ || ( maxBits = bufferParser . readUInt32BE ( ) ) === undefined ) {
1469+ bufferParser . clear ( ) ;
1470+ return doFatalError (
1471+ this . _protocol ,
1472+ 'Received malformed KEXDH_GEX_REQUEST' ,
1473+ 'handshake' ,
1474+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1475+ ) ;
1476+ }
1477+ bufferParser . clear ( ) ;
1478+
1479+ // Async callback: (err, prime, generator)
1480+ this . _protocol . _getDHParams (
1481+ minBits ,
1482+ prefBits ,
1483+ maxBits ,
1484+ ( err , prime , generator ) => {
1485+ if ( err ) {
1486+ return doFatalError (
1487+ this . _protocol ,
1488+ err . message || 'No matching prime for KEXDH_GEX_REQUEST' ,
1489+ 'handshake' ,
1490+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1491+ ) ;
1492+ }
1493+
1494+ this . _minBits = minBits ;
1495+ this . _prefBits = prefBits ;
1496+ this . _maxBits = maxBits ;
1497+
1498+ this . setDHParams ( prime , generator ) ;
1499+ this . generateKeys ( ) ;
1500+ const dh = this . getDHParams ( ) ;
1501+
1502+ this . _protocol . _debug && this . _protocol . _debug (
1503+ 'Outbound: Sending KEXDH_GEX_GROUP'
1504+ ) ;
1505+
1506+ let p = this . _protocol . _packetRW . write . allocStartKEX ;
1507+ const packet =
1508+ this . _protocol . _packetRW . write . alloc (
1509+ 1 + 4 + dh . prime . length + 4 + dh . generator . length , true ) ;
1510+ packet [ p ] = MESSAGE . KEXDH_GEX_GROUP ;
1511+ writeUInt32BE ( packet , dh . prime . length , ++ p ) ;
1512+ packet . set ( dh . prime , p += 4 ) ;
1513+ writeUInt32BE ( packet , dh . generator . length ,
1514+ p += dh . prime . length ) ;
1515+ packet . set ( dh . generator , p += 4 ) ;
1516+ this . _protocol . _cipher . encrypt (
1517+ this . _protocol . _packetRW . write . finalize ( packet , true )
1518+ ) ;
1519+
1520+ ++ this . _step ;
1521+ }
14551522 ) ;
1456- }
1523+ return ;
14571524
1458- this . _protocol . _debug && this . _protocol . _debug (
1459- 'Received DH GEX Group'
1460- ) ;
1525+ } else {
14611526
1462- /*
1463- byte SSH_MSG_KEX_DH_GEX_GROUP
1464- mpint p, safe prime
1465- mpint g, generator for subgroup in GF(p)
1466- */
1467- bufferParser . init ( payload , 1 ) ;
1468- let prime ;
1469- let gen ;
1470- if ( ( prime = bufferParser . readString ( ) ) === undefined
1471- || ( gen = bufferParser . readString ( ) ) === undefined ) {
1472- bufferParser . clear ( ) ;
1473- return doFatalError (
1474- this . _protocol ,
1475- 'Received malformed KEXDH_GEX_GROUP' ,
1476- 'handshake' ,
1477- DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1527+ // Client
1528+ if ( type !== MESSAGE . KEXDH_GEX_GROUP ) {
1529+ return doFatalError (
1530+ this . _protocol ,
1531+ `Received packet ${ type } instead of ${ MESSAGE . KEXDH_GEX_GROUP } ` ,
1532+ 'handshake' ,
1533+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1534+ ) ;
1535+ }
1536+
1537+ this . _protocol . _debug && this . _protocol . _debug (
1538+ 'Received DH GEX Group'
14781539 ) ;
1479- }
1480- bufferParser . clear ( ) ;
14811540
1482- // TODO: validate prime
1483- this . setDHParams ( prime , gen ) ;
1484- this . generateKeys ( ) ;
1485- const pubkey = this . getPublicKey ( ) ;
1541+ /*
1542+ byte SSH_MSG_KEX_DH_GEX_GROUP
1543+ mpint p, safe prime
1544+ mpint g, generator for subgroup in GF(p)
1545+ */
1546+ bufferParser . init ( payload , 1 ) ;
1547+ let prime ;
1548+ let gen ;
1549+ if ( ( prime = bufferParser . readString ( ) ) === undefined
1550+ || ( gen = bufferParser . readString ( ) ) === undefined ) {
1551+ bufferParser . clear ( ) ;
1552+ return doFatalError (
1553+ this . _protocol ,
1554+ 'Received malformed KEXDH_GEX_GROUP' ,
1555+ 'handshake' ,
1556+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1557+ ) ;
1558+ }
1559+ bufferParser . clear ( ) ;
14861560
1487- this . _protocol . _debug && this . _protocol . _debug (
1488- 'Outbound: Sending KEXDH_GEX_INIT'
1489- ) ;
1561+ // TODO: validate prime
1562+ this . setDHParams ( prime , gen ) ;
1563+ this . generateKeys ( ) ;
1564+ const pubkey = this . getPublicKey ( ) ;
14901565
1491- let p = this . _protocol . _packetRW . write . allocStartKEX ;
1492- const packet =
1493- this . _protocol . _packetRW . write . alloc ( 1 + 4 + pubkey . length , true ) ;
1494- packet [ p ] = MESSAGE . KEXDH_GEX_INIT ;
1495- writeUInt32BE ( packet , pubkey . length , ++ p ) ;
1496- packet . set ( pubkey , p += 4 ) ;
1497- this . _protocol . _cipher . encrypt (
1498- this . _protocol . _packetRW . write . finalize ( packet , true )
1499- ) ;
1566+ this . _protocol . _debug && this . _protocol . _debug (
1567+ 'Outbound: Sending KEXDH_GEX_INIT'
1568+ ) ;
15001569
1570+ let p = this . _protocol . _packetRW . write . allocStartKEX ;
1571+ const packet =
1572+ this . _protocol . _packetRW . write . alloc ( 1 + 4 + pubkey . length , true ) ;
1573+ packet [ p ] = MESSAGE . KEXDH_GEX_INIT ;
1574+ writeUInt32BE ( packet , pubkey . length , ++ p ) ;
1575+ packet . set ( pubkey , p += 4 ) ;
1576+ this . _protocol . _cipher . encrypt (
1577+ this . _protocol . _packetRW . write . finalize ( packet , true )
1578+ ) ;
1579+ }
15011580 ++ this . _step ;
15021581 break ;
15031582 }
15041583 case 2 :
15051584 if ( this . _protocol . _server ) {
1585+
1586+ // Server
15061587 if ( type !== MESSAGE . KEXDH_GEX_INIT ) {
15071588 return doFatalError (
15081589 this . _protocol ,
@@ -1511,30 +1592,92 @@ const createKeyExchange = (() => {
15111592 DISCONNECT_REASON . KEY_EXCHANGE_FAILED
15121593 ) ;
15131594 }
1595+
15141596 this . _protocol . _debug && this . _protocol . _debug (
15151597 'Received DH GEX Init'
15161598 ) ;
1517- return doFatalError (
1518- this . _protocol ,
1519- 'Group exchange not implemented for server' ,
1520- 'handshake' ,
1521- DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1599+
1600+ /*
1601+ byte SSH_MSG_KEX_DH_GEX_INIT
1602+ mpint e
1603+ */
1604+ bufferParser . init ( payload , 1 ) ;
1605+ let dhData ;
1606+ if ( ( dhData = bufferParser . readString ( ) ) === undefined ) {
1607+ bufferParser . clear ( ) ;
1608+ return doFatalError (
1609+ this . _protocol ,
1610+ 'Received malformed KEXDH_GEX_INIT' ,
1611+ 'handshake' ,
1612+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1613+ ) ;
1614+ }
1615+ bufferParser . clear ( ) ;
1616+
1617+ this . _dhData = dhData ;
1618+
1619+ let hostKey =
1620+ this . _protocol . _hostKeys [ this . negotiated . serverHostKey ] ;
1621+ if ( Array . isArray ( hostKey ) )
1622+ hostKey = hostKey [ 0 ] ;
1623+ this . _hostKey = hostKey ;
1624+
1625+ this . finish ( ) ;
1626+
1627+ } else {
1628+
1629+ // Client
1630+ if ( type !== MESSAGE . KEXDH_GEX_REPLY ) {
1631+ return doFatalError (
1632+ this . _protocol ,
1633+ `Received packet ${ type } instead of ${ MESSAGE . KEXDH_GEX_REPLY } ` ,
1634+ 'handshake' ,
1635+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
1636+ ) ;
1637+ }
1638+
1639+ this . _protocol . _debug && this . _protocol . _debug (
1640+ 'Received DH GEX Reply'
15221641 ) ;
1523- } else if ( type !== MESSAGE . KEXDH_GEX_REPLY ) {
1642+ this . _step = 1 ;
1643+ payload [ 0 ] = MESSAGE . KEXDH_REPLY ;
1644+ this . parse = KeyExchange . prototype . parse ;
1645+ return this . parse ( payload ) ;
1646+ }
1647+
1648+ ++ this . _step ;
1649+ break ;
1650+
1651+ case 3 :
1652+
1653+ if ( type !== MESSAGE . NEWKEYS ) {
15241654 return doFatalError (
15251655 this . _protocol ,
1526- `Received packet ${ type } instead of ${ MESSAGE . KEXDH_GEX_REPLY } ` ,
1656+ `Received packet ${ type } instead of ${ MESSAGE . NEWKEYS } ` ,
15271657 'handshake' ,
15281658 DISCONNECT_REASON . KEY_EXCHANGE_FAILED
15291659 ) ;
15301660 }
15311661 this . _protocol . _debug && this . _protocol . _debug (
1532- 'Received DH GEX Reply'
1662+ 'Inbound: NEWKEYS'
1663+ ) ;
1664+ this . _receivedNEWKEYS = true ;
1665+ if ( this . _protocol . _strictMode )
1666+ this . _protocol . _decipher . inSeqno = 0 ;
1667+ ++ this . _step ;
1668+ if ( this . _protocol . _server || this . _hostVerified )
1669+ return this . finish ( ) ;
1670+
1671+ // Signal to current decipher that we need to change to a new decipher
1672+ // for the next packet
1673+ return false ;
1674+ default :
1675+ return doFatalError (
1676+ this . _protocol ,
1677+ `Received unexpected packet ${ type } after NEWKEYS` ,
1678+ 'handshake' ,
1679+ DISCONNECT_REASON . KEY_EXCHANGE_FAILED
15331680 ) ;
1534- this . _step = 1 ;
1535- payload [ 0 ] = MESSAGE . KEXDH_REPLY ;
1536- this . parse = KeyExchange . prototype . parse ;
1537- this . parse ( payload ) ;
15381681 }
15391682 }
15401683 }
0 commit comments