@electerm/ssh2 1.17.1 → 1.17.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/protocol/Protocol.js +129 -101
- package/package.json +1 -1
package/lib/protocol/Protocol.js
CHANGED
|
@@ -629,35 +629,65 @@ class Protocol {
|
|
|
629
629
|
sendPacket(this, this._packetRW.write.finalize(packet));
|
|
630
630
|
}
|
|
631
631
|
authPK(username, pubKey, keyAlgo, cbSign) {
|
|
632
|
+
if (pubKey && pubKey.certificate) {
|
|
633
|
+
return this.authPKCert(username, pubKey, keyAlgo, cbSign);
|
|
634
|
+
}
|
|
635
|
+
if (this._server)
|
|
636
|
+
throw new Error('Client-only method called in server mode');
|
|
637
|
+
|
|
638
|
+
pubKey = parseKey(pubKey);
|
|
639
|
+
if (pubKey instanceof Error)
|
|
640
|
+
throw new Error('Invalid key');
|
|
641
|
+
|
|
642
|
+
let keyType = pubKey.type;
|
|
643
|
+
if (keyType === 'ssh-rsa') {
|
|
644
|
+
for (const algo of ['rsa-sha2-512', 'rsa-sha2-256']) {
|
|
645
|
+
if (this._remoteHostKeyAlgorithms.includes(algo)) {
|
|
646
|
+
keyType = algo;
|
|
647
|
+
break;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
pubKey = pubKey.getPublicSSH();
|
|
652
|
+
|
|
653
|
+
if (typeof keyAlgo === 'function') {
|
|
654
|
+
cbSign = keyAlgo;
|
|
655
|
+
keyAlgo = undefined;
|
|
656
|
+
}
|
|
657
|
+
if (!keyAlgo)
|
|
658
|
+
keyAlgo = keyType;
|
|
659
|
+
|
|
660
|
+
// For standard public keys, the algorithm name on the wire (keyAlgo)
|
|
661
|
+
// is the same as the algorithm used for signing (signAlgo).
|
|
662
|
+
this._authPKCommon(username, keyAlgo, pubKey, keyAlgo, cbSign);
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
authPKCert(username, pubKey, keyAlgo, cbSign) {
|
|
632
666
|
if (this._server)
|
|
633
667
|
throw new Error('Client-only method called in server mode');
|
|
634
668
|
|
|
635
|
-
const origPubKey = pubKey;
|
|
636
|
-
|
|
637
669
|
// Only parse if it's not already a parsed key object
|
|
638
|
-
|
|
639
|
-
if (typeof pubKey !== 'object' || pubKey === null ||
|
|
670
|
+
if (typeof pubKey !== 'object' || pubKey === null ||
|
|
640
671
|
typeof pubKey.type !== 'string' || typeof pubKey.getPublicSSH !== 'function') {
|
|
641
672
|
pubKey = parseKey(pubKey);
|
|
642
673
|
if (pubKey instanceof Error)
|
|
643
674
|
throw new Error('Invalid key');
|
|
644
675
|
}
|
|
645
676
|
|
|
646
|
-
let keyType = pubKey.type;
|
|
647
|
-
|
|
648
677
|
// Check if this is a certificate-wrapped key
|
|
649
|
-
let pubKeyData
|
|
678
|
+
let pubKeyData;
|
|
650
679
|
let isCertificate = false;
|
|
651
680
|
if (pubKey.getCertificateBuffer) {
|
|
652
|
-
// This is a CertificateKey, use the certificate data instead
|
|
653
681
|
pubKeyData = pubKey.getCertificateBuffer();
|
|
654
682
|
isCertificate = true;
|
|
655
683
|
this._debug && this._debug('Using SSH certificate for authentication');
|
|
684
|
+
} else {
|
|
685
|
+
pubKeyData = pubKey.getPublicSSH();
|
|
656
686
|
}
|
|
657
687
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
//
|
|
688
|
+
let keyType = pubKey.type;
|
|
689
|
+
|
|
690
|
+
// Determine the base signing algorithm (upgrading RSA if needed)
|
|
661
691
|
let signAlgo = keyType;
|
|
662
692
|
if (keyType === 'ssh-rsa') {
|
|
663
693
|
for (const algo of ['rsa-sha2-256', 'rsa-sha2-512']) {
|
|
@@ -667,12 +697,10 @@ class Protocol {
|
|
|
667
697
|
}
|
|
668
698
|
}
|
|
669
699
|
}
|
|
670
|
-
|
|
671
|
-
//
|
|
672
|
-
// but keep signAlgo as the base signing algorithm
|
|
700
|
+
|
|
701
|
+
// Determine the algorithm name to use in the packet (keyAlgo)
|
|
673
702
|
if (isCertificate) {
|
|
674
703
|
// Map the signing algorithm to certificate algorithm
|
|
675
|
-
// e.g., rsa-sha2-512 -> rsa-sha2-512-cert-v01@openssh.com
|
|
676
704
|
if (signAlgo === 'rsa-sha2-512') {
|
|
677
705
|
keyType = 'rsa-sha2-512-cert-v01@openssh.com';
|
|
678
706
|
} else if (signAlgo === 'rsa-sha2-256') {
|
|
@@ -692,7 +720,6 @@ class Protocol {
|
|
|
692
720
|
}
|
|
693
721
|
this._debug && this._debug(`Certificate key algorithm: ${keyType}`);
|
|
694
722
|
} else {
|
|
695
|
-
// For non-certificates, keyType should be signAlgo
|
|
696
723
|
keyType = signAlgo;
|
|
697
724
|
}
|
|
698
725
|
|
|
@@ -700,118 +727,119 @@ class Protocol {
|
|
|
700
727
|
cbSign = keyAlgo;
|
|
701
728
|
keyAlgo = undefined;
|
|
702
729
|
}
|
|
703
|
-
|
|
704
|
-
// For certificates,
|
|
730
|
+
|
|
731
|
+
// For certificates, default to the determined cert type if not provided
|
|
705
732
|
if (isCertificate) {
|
|
706
733
|
keyAlgo = keyType;
|
|
707
734
|
} else if (!keyAlgo) {
|
|
708
735
|
keyAlgo = keyType;
|
|
709
736
|
}
|
|
710
737
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
// For certificates, signAlgo is the base signing algorithm (e.g., rsa-sha2-512)
|
|
714
|
-
// For non-certificates, signAlgo equals keyAlgo
|
|
715
|
-
const signAlgoLen = Buffer.byteLength(signAlgo);
|
|
716
|
-
const pubKeyLen = pubKeyData.length;
|
|
717
|
-
const sessionID = this._kex.sessionID;
|
|
718
|
-
const sesLen = sessionID.length;
|
|
719
|
-
const payloadLen =
|
|
720
|
-
(cbSign ? 4 + sesLen : 0)
|
|
721
|
-
+ 1 + 4 + userLen + 4 + 14 + 4 + 9 + 1 + 4 + algoLen + 4 + pubKeyLen;
|
|
722
|
-
let packet;
|
|
723
|
-
let p;
|
|
724
|
-
if (cbSign) {
|
|
725
|
-
packet = Buffer.allocUnsafe(payloadLen);
|
|
726
|
-
p = 0;
|
|
727
|
-
writeUInt32BE(packet, sesLen, p);
|
|
728
|
-
packet.set(sessionID, p += 4);
|
|
729
|
-
p += sesLen;
|
|
730
|
-
} else {
|
|
731
|
-
packet = this._packetRW.write.alloc(payloadLen);
|
|
732
|
-
p = this._packetRW.write.allocStart;
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
packet[p] = MESSAGE.USERAUTH_REQUEST;
|
|
736
|
-
|
|
737
|
-
writeUInt32BE(packet, userLen, ++p);
|
|
738
|
-
packet.utf8Write(username, p += 4, userLen);
|
|
739
|
-
|
|
740
|
-
writeUInt32BE(packet, 14, p += userLen);
|
|
741
|
-
packet.utf8Write('ssh-connection', p += 4, 14);
|
|
742
|
-
|
|
743
|
-
writeUInt32BE(packet, 9, p += 14);
|
|
744
|
-
packet.utf8Write('publickey', p += 4, 9);
|
|
745
|
-
|
|
746
|
-
packet[p += 9] = (cbSign ? 1 : 0);
|
|
747
|
-
|
|
748
|
-
writeUInt32BE(packet, algoLen, ++p);
|
|
749
|
-
packet.utf8Write(keyAlgo, p += 4, algoLen);
|
|
738
|
+
this._authPKCommon(username, keyAlgo, pubKeyData, signAlgo, cbSign);
|
|
739
|
+
}
|
|
750
740
|
|
|
751
|
-
|
|
752
|
-
|
|
741
|
+
_authPKCommon(username, algo, pubKey, signAlgo, cbSign) {
|
|
742
|
+
const userLen = Buffer.byteLength(username);
|
|
743
|
+
const algoLen = Buffer.byteLength(algo);
|
|
744
|
+
const pubKeyLen = pubKey.length;
|
|
745
|
+
|
|
746
|
+
// Calculate payload length for the basic packet
|
|
747
|
+
// Header: MSG_USERAUTH_REQUEST (1) + user (4+len) + service (4+14) + method (4+9) + sign_flag (1) + algo (4+len) + key (4+len)
|
|
748
|
+
const basePayloadLen = 1 + 4 + userLen + 4 + 14 + 4 + 9 + 1 + 4 + algoLen + 4 + pubKeyLen;
|
|
753
749
|
|
|
754
750
|
if (!cbSign) {
|
|
755
|
-
|
|
751
|
+
// -----------------------------------------------------------------------
|
|
752
|
+
// Send "Check" Packet (Signature Flag = 0)
|
|
753
|
+
// -----------------------------------------------------------------------
|
|
754
|
+
const packet = this._packetRW.write.alloc(basePayloadLen);
|
|
755
|
+
let p = this._packetRW.write.allocStart;
|
|
756
756
|
|
|
757
|
-
this._debug && this._debug(
|
|
758
|
-
'Outbound: Sending USERAUTH_REQUEST (publickey -- check)'
|
|
759
|
-
);
|
|
760
|
-
sendPacket(this, this._packetRW.write.finalize(packet));
|
|
761
|
-
return;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
cbSign(packet, (signature) => {
|
|
765
|
-
signature = convertSignature(signature, signAlgo);
|
|
766
|
-
if (signature === false)
|
|
767
|
-
throw new Error('Error while converting handshake signature');
|
|
768
|
-
|
|
769
|
-
const sigLen = signature.length;
|
|
770
|
-
p = this._packetRW.write.allocStart;
|
|
771
|
-
packet = this._packetRW.write.alloc(
|
|
772
|
-
1 + 4 + userLen + 4 + 14 + 4 + 9 + 1 + 4 + algoLen + 4 + pubKeyLen + 4
|
|
773
|
-
+ 4 + signAlgoLen + 4 + sigLen
|
|
774
|
-
);
|
|
775
|
-
|
|
776
|
-
// TODO: simply copy from original "packet" to new `packet` to avoid
|
|
777
|
-
// having to write each individual field a second time?
|
|
778
757
|
packet[p] = MESSAGE.USERAUTH_REQUEST;
|
|
779
|
-
|
|
780
758
|
writeUInt32BE(packet, userLen, ++p);
|
|
781
759
|
packet.utf8Write(username, p += 4, userLen);
|
|
782
|
-
|
|
783
760
|
writeUInt32BE(packet, 14, p += userLen);
|
|
784
761
|
packet.utf8Write('ssh-connection', p += 4, 14);
|
|
785
|
-
|
|
786
762
|
writeUInt32BE(packet, 9, p += 14);
|
|
787
763
|
packet.utf8Write('publickey', p += 4, 9);
|
|
788
|
-
|
|
789
|
-
packet[p += 9] = 1;
|
|
790
|
-
|
|
764
|
+
packet[p += 9] = 0; // Not signed
|
|
791
765
|
writeUInt32BE(packet, algoLen, ++p);
|
|
792
|
-
packet.utf8Write(
|
|
793
|
-
|
|
766
|
+
packet.utf8Write(algo, p += 4, algoLen);
|
|
794
767
|
writeUInt32BE(packet, pubKeyLen, p += algoLen);
|
|
795
|
-
packet.set(
|
|
768
|
+
packet.set(pubKey, p += 4);
|
|
796
769
|
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
770
|
+
this._authsQueue.push('publickey');
|
|
771
|
+
this._debug && this._debug('Outbound: Sending USERAUTH_REQUEST (publickey -- check)');
|
|
772
|
+
sendPacket(this, this._packetRW.write.finalize(packet));
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
801
775
|
|
|
776
|
+
// -------------------------------------------------------------------------
|
|
777
|
+
// Signing Process
|
|
778
|
+
// -------------------------------------------------------------------------
|
|
779
|
+
const sessionID = this._kex.sessionID;
|
|
780
|
+
const sesLen = sessionID.length;
|
|
781
|
+
|
|
782
|
+
// Construct the data payload to be signed: SessionID + UserAuth Request Packet
|
|
783
|
+
// (Note: The packet construction here mimics the one above but sets sign_flag = 1)
|
|
784
|
+
const dataToSignLen = 4 + sesLen + basePayloadLen;
|
|
785
|
+
const dataToSign = Buffer.allocUnsafe(dataToSignLen);
|
|
786
|
+
let p = 0;
|
|
787
|
+
|
|
788
|
+
writeUInt32BE(dataToSign, sesLen, p);
|
|
789
|
+
dataToSign.set(sessionID, p += 4);
|
|
790
|
+
p += sesLen;
|
|
791
|
+
|
|
792
|
+
dataToSign[p] = MESSAGE.USERAUTH_REQUEST;
|
|
793
|
+
writeUInt32BE(dataToSign, userLen, ++p);
|
|
794
|
+
dataToSign.utf8Write(username, p += 4, userLen);
|
|
795
|
+
writeUInt32BE(dataToSign, 14, p += userLen);
|
|
796
|
+
dataToSign.utf8Write('ssh-connection', p += 4, 14);
|
|
797
|
+
writeUInt32BE(dataToSign, 9, p += 14);
|
|
798
|
+
dataToSign.utf8Write('publickey', p += 4, 9);
|
|
799
|
+
dataToSign[p += 9] = 1; // Signed
|
|
800
|
+
writeUInt32BE(dataToSign, algoLen, ++p);
|
|
801
|
+
dataToSign.utf8Write(algo, p += 4, algoLen);
|
|
802
|
+
writeUInt32BE(dataToSign, pubKeyLen, p += algoLen);
|
|
803
|
+
dataToSign.set(pubKey, p += 4);
|
|
804
|
+
|
|
805
|
+
cbSign(dataToSign, (signature) => {
|
|
806
|
+
signature = convertSignature(signature, signAlgo);
|
|
807
|
+
if (signature === false)
|
|
808
|
+
throw new Error('Error while converting handshake signature');
|
|
809
|
+
|
|
810
|
+
const sigLen = signature.length;
|
|
811
|
+
const signAlgoLen = Buffer.byteLength(signAlgo);
|
|
812
|
+
|
|
813
|
+
// -----------------------------------------------------------------------
|
|
814
|
+
// Send Signed Packet
|
|
815
|
+
// -----------------------------------------------------------------------
|
|
816
|
+
// We reconstruct the packet to send. It is identical to the data part of
|
|
817
|
+
// dataToSign (minus SessionID), plus the signature blob appended at the end.
|
|
818
|
+
|
|
819
|
+
const totalPacketLen = basePayloadLen + 4 + 4 + signAlgoLen + 4 + sigLen;
|
|
820
|
+
p = this._packetRW.write.allocStart;
|
|
821
|
+
const packet = this._packetRW.write.alloc(totalPacketLen);
|
|
822
|
+
|
|
823
|
+
// Copy the standard fields from our signing buffer (skipping sessionID)
|
|
824
|
+
// Offset of packet start in dataToSign is 4 + sesLen
|
|
825
|
+
const packetStartInSigData = 4 + sesLen;
|
|
826
|
+
bufferCopy(dataToSign, packet, packetStartInSigData, dataToSign.length, p);
|
|
827
|
+
|
|
828
|
+
// Move pointer to end of copied data
|
|
829
|
+
p += (dataToSign.length - packetStartInSigData);
|
|
830
|
+
|
|
831
|
+
// Append Signature Blob
|
|
832
|
+
// Length of signature structure (string len + string + blob len + blob)
|
|
833
|
+
writeUInt32BE(packet, 4 + signAlgoLen + 4 + sigLen, p);
|
|
834
|
+
|
|
802
835
|
writeUInt32BE(packet, signAlgoLen, p += 4);
|
|
803
836
|
packet.utf8Write(signAlgo, p += 4, signAlgoLen);
|
|
804
|
-
|
|
837
|
+
|
|
805
838
|
writeUInt32BE(packet, sigLen, p += signAlgoLen);
|
|
806
839
|
packet.set(signature, p += 4);
|
|
807
840
|
|
|
808
|
-
// Servers shouldn't send packet type 60 in response to signed publickey
|
|
809
|
-
// attempts, but if they do, interpret as type 60.
|
|
810
841
|
this._authsQueue.push('publickey');
|
|
811
|
-
|
|
812
|
-
this._debug && this._debug(
|
|
813
|
-
'Outbound: Sending USERAUTH_REQUEST (publickey)'
|
|
814
|
-
);
|
|
842
|
+
this._debug && this._debug('Outbound: Sending USERAUTH_REQUEST (publickey)');
|
|
815
843
|
sendPacket(this, this._packetRW.write.finalize(packet));
|
|
816
844
|
});
|
|
817
845
|
}
|
package/package.json
CHANGED