ソースを参照

Updating code to PuTTY Pre-release 0.83:2025-01-03.1e45199 + Updating PuTTY code to C++ Builder

Source commit: f19842107de91d45c4a819260d5d335a7635980c
Martin Prikryl 9 ヶ月 前
コミット
59a148c152

+ 6 - 0
source/Putty.cbproj

@@ -160,6 +160,9 @@
         <CppCompile Include="putty\crypto\hmac.c">
             <BuildOrder>23</BuildOrder>
         </CppCompile>
+        <CppCompile Include="putty\crypto\kex-hybrid.c">
+            <BuildOrder>174</BuildOrder>
+        </CppCompile>
         <CppCompile Include="putty\crypto\mac.c">
             <BuildOrder>25</BuildOrder>
         </CppCompile>
@@ -169,6 +172,9 @@
         <CppCompile Include="putty\crypto\md5.c">
             <BuildOrder>27</BuildOrder>
         </CppCompile>
+        <CppCompile Include="putty\crypto\mlkem.c">
+            <BuildOrder>175</BuildOrder>
+        </CppCompile>
         <CppCompile Include="putty\crypto\mpint.c">
             <BuildOrder>28</BuildOrder>
         </CppCompile>

+ 1 - 1
source/core/PuttyIntf.cpp

@@ -1405,7 +1405,7 @@ TStrings * SshKexList()
   // Same order as DefaultKexList
   const ssh_kexes * Kexes[] = {
     &ssh_gssk5_ecdh_kex, &ssh_gssk5_sha2_kex, &ssh_gssk5_sha1_kex,
-    &ssh_ntru_hybrid_kex, &ssh_ecdh_kex, &ssh_diffiehellman_gex,
+    &ssh_ntru_hybrid_kex, &ssh_mlkem_curve25519_hybrid_kex, &ssh_mlkem_nist_hybrid_kex, &ssh_ecdh_kex, &ssh_diffiehellman_gex,
     &ssh_diffiehellman_group18, &ssh_diffiehellman_group17, &ssh_diffiehellman_group16, &ssh_diffiehellman_group15, &ssh_diffiehellman_group14,
     &ssh_rsa_kex, &ssh_diffiehellman_group1 };
   for (unsigned int Index = 0; Index < LENOF(Kexes); Index++)

+ 2 - 0
source/core/SecureShell.cpp

@@ -218,6 +218,8 @@ Conf * __fastcall TSecureShell::StoreToConfig(TSessionData * Data, bool Simple)
       case kexRSA: pkex = KEX_RSA; break;
       case kexECDH: pkex = KEX_ECDH; break;
       case kexNTRUHybrid: pkex = KEX_NTRU_HYBRID; break;
+      case kexMLKEM25519Hybrid: pkex = KEX_MLKEM_25519_HYBRID; break;
+      case kexMLKEMNISTHybrid: pkex = KEX_MLKEM_NIST_HYBRID; break;
       default: DebugFail();
     }
     conf_set_int_int(conf, CONF_ssh_kexlist, k, pkex);

+ 2 - 2
source/core/SessionData.cpp

@@ -34,7 +34,7 @@ const wchar_t * ProxyMethodNames = L"None;SOCKS4;SOCKS5;HTTP;Telnet;Cmd";
 TIntMapping ProxyMethodMapping = CreateIntMappingFromEnumNames(LowerCase(ProxyMethodNames));
 const wchar_t * DefaultName = L"Default Settings";
 const UnicodeString CipherNames[CIPHER_COUNT] = {L"WARN", L"3des", L"blowfish", L"aes", L"des", L"arcfour", L"chacha20", "aesgcm"};
-const UnicodeString KexNames[KEX_COUNT] = {L"WARN", L"dh-group1-sha1", L"dh-group14-sha1", L"dh-group15-sha512", L"dh-group16-sha512", L"dh-group17-sha512", L"dh-group18-sha512", L"dh-gex-sha1", L"rsa", L"ecdh", L"ntru-curve25519"};
+const UnicodeString KexNames[KEX_COUNT] = {L"WARN", L"dh-group1-sha1", L"dh-group14-sha1", L"dh-group15-sha512", L"dh-group16-sha512", L"dh-group17-sha512", L"dh-group18-sha512", L"dh-gex-sha1", L"rsa", L"ecdh", L"ntru-curve25519", L"mlkem-curve25519", L"mlkem-nist"};
 const UnicodeString HostKeyNames[HOSTKEY_COUNT] = {L"WARN", L"rsa", L"dsa", L"ecdsa", L"ed25519", L"ed448"};
 const UnicodeString GssLibNames[GSSLIB_COUNT] = {L"gssapi32", L"sspi", L"custom"};
 // Update also order in SshCipherList()
@@ -42,7 +42,7 @@ const TCipher DefaultCipherList[CIPHER_COUNT] =
   { cipAES, cipChaCha20, cipAESGCM, cip3DES, cipWarn, cipDES, cipBlowfish, cipArcfour };
 // Update also order in SshKexList()
 const TKex DefaultKexList[KEX_COUNT] =
-  { kexNTRUHybrid, kexECDH, kexDHGEx, kexDHGroup18, kexDHGroup17, kexDHGroup16, kexDHGroup15, kexDHGroup14, kexRSA, kexWarn, kexDHGroup1 };
+  { kexNTRUHybrid, kexMLKEM25519Hybrid, kexMLKEMNISTHybrid, kexECDH, kexDHGEx, kexDHGroup18, kexDHGroup17, kexDHGroup16, kexDHGroup15, kexDHGroup14, kexRSA, kexWarn, kexDHGroup1 };
 const THostKey DefaultHostKeyList[HOSTKEY_COUNT] =
   { hkED448, hkED25519, hkECDSA, hkRSA, hkDSA, hkWarn };
 const TGssLib DefaultGssLibList[GSSLIB_COUNT] =

+ 1 - 1
source/core/SessionData.h

@@ -17,7 +17,7 @@ enum TFSProtocol { fsSCPonly = 0, fsSFTP = 1, fsSFTPonly = 2, fsFTP = 5, fsWebDA
 #define FSPROTOCOL_COUNT (fsS3+1)
 extern const wchar_t * ProxyMethodNames;
 enum TProxyMethod { pmNone, pmSocks4, pmSocks5, pmHTTP, pmTelnet, pmCmd };
-enum TKex { kexWarn, kexDHGroup1, kexDHGroup14, kexDHGroup15, kexDHGroup16, kexDHGroup17, kexDHGroup18, kexDHGEx, kexRSA, kexECDH, kexNTRUHybrid, kexCount };
+enum TKex { kexWarn, kexDHGroup1, kexDHGroup14, kexDHGroup15, kexDHGroup16, kexDHGroup17, kexDHGroup18, kexDHGEx, kexRSA, kexECDH, kexNTRUHybrid, kexMLKEM25519Hybrid, kexMLKEMNISTHybrid, kexCount };
 #define KEX_COUNT (kexCount)
 enum THostKey { hkWarn, hkRSA, hkDSA, hkECDSA, hkED25519, hkED448, hkCount };
 #define HOSTKEY_COUNT (hkCount)

+ 1 - 1
source/forms/SiteAdvanced.cpp

@@ -279,7 +279,7 @@ void __fastcall TSiteAdvancedDialog::LoadSession()
     // KEX page
 
     KexListBox->Items->Clear();
-    DebugAssert(KEX_NAME_WARN + KEX_COUNT - 1 == KEX_NAME_NTRU_HYBRID);
+    DebugAssert(KEX_NAME_WARN + KEX_COUNT - 1 == KEX_NAME_MLKEM_NIST_HYBRID);
     for (int Index = 0; Index < KEX_COUNT; Index++)
     {
       KexListBox->Items->AddObject(

+ 7 - 6
source/forms/SiteAdvanced.dfm

@@ -2148,13 +2148,13 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
           Left = 0
           Top = 6
           Width = 393
-          Height = 222
+          Height = 248
           Anchors = [akLeft, akTop, akRight]
           Caption = 'Key exchange algorithm options'
           TabOrder = 0
           DesignSize = (
             393
-            222)
+            248)
           object Label28: TLabel
             Left = 12
             Top = 19
@@ -2167,8 +2167,8 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
             Left = 12
             Top = 36
             Width = 285
-            Height = 153
-            Anchors = [akLeft, akTop, akRight]
+            Height = 179
+            Anchors = [akLeft, akTop, akRight, akBottom]
             DragMode = dmAutomatic
             ItemHeight = 13
             TabOrder = 0
@@ -2199,9 +2199,10 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
           end
           object AuthGSSAPIKEXCheck: TCheckBox
             Left = 12
-            Top = 195
+            Top = 221
             Width = 285
             Height = 17
+            Anchors = [akLeft, akBottom]
             Caption = 'Attempt &GSSAPI key exchange'
             TabOrder = 3
             OnClick = DataChange
@@ -2209,7 +2210,7 @@ object SiteAdvancedDialog: TSiteAdvancedDialog
         end
         object KexReexchangeGroup: TGroupBox
           Left = 0
-          Top = 235
+          Top = 261
           Width = 393
           Height = 69
           Anchors = [akLeft, akTop, akRight]

+ 2 - 2
source/putty/crypto/ecc-ssh.c

@@ -1831,7 +1831,7 @@ static const ecdh_keyalg ssh_ecdhkex_m_alg = {
     /*.getpublic =*/ ssh_ecdhkex_m_getpublic,
     /*.getkey =*/ ssh_ecdhkex_m_getkey,
     /*.description =*/ ssh_ecdhkex_description,
-    .packet_naming_ctx = SSH2_PKTCTX_ECDHKEX,
+    /*.packet_naming_ctx =*/ SSH2_PKTCTX_ECDHKEX,
 };
 const ssh_kex ssh_ec_kex_curve25519 = {
     /*.name =*/ "curve25519-sha256",
@@ -1876,7 +1876,7 @@ static const ecdh_keyalg ssh_ecdhkex_w_alg = {
     /*.getpublic =*/ ssh_ecdhkex_w_getpublic,
     /*.getkey =*/ ssh_ecdhkex_w_getkey,
     /*.description =*/ ssh_ecdhkex_description,
-    .packet_naming_ctx = SSH2_PKTCTX_ECDHKEX,
+    /*.packet_naming_ctx =*/ SSH2_PKTCTX_ECDHKEX,
 };
 static const struct eckex_extra kex_extra_nistp256 = { ec_p256 };
 const ssh_kex ssh_ec_kex_nistp256 = {

+ 84 - 57
source/putty/crypto/kex-hybrid.c

@@ -51,12 +51,17 @@ static void reformat_mpint_be(ptrlen input, BinarySink *output, size_t bytes)
 {
     BinarySource src[1];
     BinarySource_BARE_INIT_PL(src, input);
+    { // WINSCP
     mp_int *mp = get_mp_ssh2(src);
     assert(!get_err(src));
     assert(get_avail(src) == 0);
-    for (size_t i = bytes; i-- > 0 ;)
+    { // WINSCP
+    size_t i; // WINSCP
+    for (i = bytes; i-- > 0 ;)
         put_byte(output, mp_get_byte(mp, i));
     mp_free(mp);
+    } // WINSCP
+    } // WINSCP
 }
 
 static void reformat_mpint_be_32(ptrlen input, BinarySink *output)
@@ -88,6 +93,7 @@ struct hybrid_client_state {
 static ecdh_key *hybrid_client_new(const ssh_kex *kex, bool is_server)
 {
     assert(!is_server);
+    { // WINSCP
     hybrid_client_state *s = snew(hybrid_client_state);
     s->alg = kex->extra;
     s->ek.vt = &hybrid_client_vt;
@@ -95,6 +101,7 @@ static ecdh_key *hybrid_client_new(const ssh_kex *kex, bool is_server)
     s->pq_dk = pq_kem_keygen(s->alg->pq_alg, BinarySink_UPCAST(s->pq_ek));
     s->classical = ecdh_key_new(s->alg->classical_alg, is_server);
     return &s->ek;
+    } // WINSCP
 }
 
 static void hybrid_client_free(ecdh_key *ek)
@@ -129,6 +136,7 @@ static bool hybrid_client_getkey(ecdh_key *ek, ptrlen remoteKey, BinarySink *bs)
     BinarySource src[1];
     BinarySource_BARE_INIT_PL(src, remoteKey);
 
+    { // WINSCP
     ssh_hash *h = ssh_hash_new(s->alg->combining_hash);
 
     ptrlen pq_ciphertext = get_data(src, s->alg->pq_alg->c_len);
@@ -141,6 +149,7 @@ static bool hybrid_client_getkey(ecdh_key *ek, ptrlen remoteKey, BinarySink *bs)
         return false;                  /* pq ciphertext didn't validate */
     }
 
+    { // WINSCP
     ptrlen classical_data = get_data(src, get_avail(src));
     strbuf *classical_key = strbuf_new();
     if (!ecdh_key_getkey(s->classical, classical_data,
@@ -155,21 +164,25 @@ static bool hybrid_client_getkey(ecdh_key *ek, ptrlen remoteKey, BinarySink *bs)
      * Finish up: compute the final output hash and return it encoded
      * as a string.
      */
+    { // WINSCP
     unsigned char hashdata[MAX_HASH_LEN];
     ssh_hash_final(h, hashdata);
     put_stringpl(bs, make_ptrlen(hashdata, s->alg->combining_hash->hlen));
     smemclr(hashdata, sizeof(hashdata));
 
     return true;
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 
 static const ecdh_keyalg hybrid_client_vt = {
-    .new = hybrid_client_new, /* but normally the selector calls this */
-    .free = hybrid_client_free,
-    .getpublic = hybrid_client_getpublic,
-    .getkey = hybrid_client_getkey,
-    .description = hybrid_description,
-    .packet_naming_ctx = SSH2_PKTCTX_HYBRIDKEX,
+    /*.new =*/ hybrid_client_new, /* but normally the selector calls this */
+    /*.free =*/ hybrid_client_free,
+    /*.getpublic =*/ hybrid_client_getpublic,
+    /*.getkey =*/ hybrid_client_getkey,
+    /*.description =*/ hybrid_description,
+    /*.packet_naming_ctx =*/ SSH2_PKTCTX_HYBRIDKEX,
 };
 
 /* ----------------------------------------------------------------------
@@ -190,12 +203,14 @@ struct hybrid_server_state {
 static ecdh_key *hybrid_server_new(const ssh_kex *kex, bool is_server)
 {
     assert(is_server);
+    { // WINSCP
     hybrid_server_state *s = snew(hybrid_server_state);
     s->alg = kex->extra;
     s->ek.vt = &hybrid_server_vt;
     s->pq_ciphertext = strbuf_new_nm();
     s->classical = ecdh_key_new(s->alg->classical_alg, is_server);
     return &s->ek;
+    } // WINSCP
 }
 
 static void hybrid_server_free(ecdh_key *ek)
@@ -219,6 +234,7 @@ static bool hybrid_server_getkey(ecdh_key *ek, ptrlen remoteKey, BinarySink *bs)
     BinarySource src[1];
     BinarySource_BARE_INIT_PL(src, remoteKey);
 
+    { // WINSCP
     ssh_hash *h = ssh_hash_new(s->alg->combining_hash);
 
     ptrlen pq_ek = get_data(src, s->alg->pq_alg->ek_len);
@@ -233,6 +249,7 @@ static bool hybrid_server_getkey(ecdh_key *ek, ptrlen remoteKey, BinarySink *bs)
         return false;                  /* pq encryption key didn't validate */
     }
 
+    { // WINSCP
     ptrlen classical_data = get_data(src, get_avail(src));
     strbuf *classical_key = strbuf_new();
     if (!ecdh_key_getkey(s->classical, classical_data,
@@ -247,12 +264,16 @@ static bool hybrid_server_getkey(ecdh_key *ek, ptrlen remoteKey, BinarySink *bs)
      * Finish up: compute the final output hash and return it encoded
      * as a string.
      */
+    { // WINSCP
     unsigned char hashdata[MAX_HASH_LEN];
     ssh_hash_final(h, hashdata);
     put_stringpl(bs, make_ptrlen(hashdata, s->alg->combining_hash->hlen));
     smemclr(hashdata, sizeof(hashdata));
 
     return true;
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 
 static void hybrid_server_getpublic(ecdh_key *ek, BinarySink *bs)
@@ -263,12 +284,12 @@ static void hybrid_server_getpublic(ecdh_key *ek, BinarySink *bs)
 }
 
 static const ecdh_keyalg hybrid_server_vt = {
-    .new = hybrid_server_new, /* but normally the selector calls this */
-    .free = hybrid_server_free,
-    .getkey = hybrid_server_getkey,
-    .getpublic = hybrid_server_getpublic,
-    .description = hybrid_description,
-    .packet_naming_ctx = SSH2_PKTCTX_HYBRIDKEX,
+    /*.new =*/ hybrid_server_new, /* but normally the selector calls this */
+    /*.free =*/ hybrid_server_free,
+    /*.getpublic =*/ hybrid_server_getpublic,
+    /*.getkey =*/ hybrid_server_getkey,
+    /*.description =*/ hybrid_description,
+    /*.packet_naming_ctx =*/ SSH2_PKTCTX_HYBRIDKEX,
 };
 
 /* ----------------------------------------------------------------------
@@ -287,9 +308,10 @@ static ecdh_key *hybrid_selector_new(const ssh_kex *kex, bool is_server)
 static const ecdh_keyalg hybrid_selector_vt = {
     /* This is a never-instantiated vtable which only implements the
      * functions that don't require an instance. */
-    .new = hybrid_selector_new,
-    .description = hybrid_description,
-    .packet_naming_ctx = SSH2_PKTCTX_HYBRIDKEX,
+    /*.new =*/ hybrid_selector_new,
+    NULL, NULL, NULL, // WINSCP
+    /*.description =*/ hybrid_description,
+    /*.packet_naming_ctx =*/ SSH2_PKTCTX_HYBRIDKEX,
 };
 
 /* ----------------------------------------------------------------------
@@ -297,26 +319,28 @@ static const ecdh_keyalg hybrid_selector_vt = {
  */
 
 static const hybrid_alg ssh_ntru_curve25519_hybrid = {
-    .combining_hash = &ssh_sha512,
-    .pq_alg = &ssh_ntru,
-    .classical_alg = &ssh_ec_kex_curve25519,
-    .reformat = reformat_mpint_be_32,
+    /*.combining_hash =*/ &ssh_sha512,
+    /*.pq_alg =*/ &ssh_ntru,
+    /*.classical_alg =*/ &ssh_ec_kex_curve25519,
+    /*.reformat =*/ reformat_mpint_be_32,
 };
 
 static const ssh_kex ssh_ntru_curve25519 = {
-    .name = "sntrup761x25519-sha512",
-    .main_type = KEXTYPE_ECDH,
-    .hash = &ssh_sha512,
-    .ecdh_vt = &hybrid_selector_vt,
-    .extra = &ssh_ntru_curve25519_hybrid,
+    /*.name =*/ "sntrup761x25519-sha512",
+    NULL, // WINSCP
+    /*.main_type =*/ KEXTYPE_ECDH,
+    /*.hash =*/ &ssh_sha512,
+    /*.ecdh_vt =*/ &hybrid_selector_vt,
+    /*.extra =*/ &ssh_ntru_curve25519_hybrid,
 };
 
 static const ssh_kex ssh_ntru_curve25519_openssh = {
-    .name = "[email protected]",
-    .main_type = KEXTYPE_ECDH,
-    .hash = &ssh_sha512,
-    .ecdh_vt = &hybrid_selector_vt,
-    .extra = &ssh_ntru_curve25519_hybrid,
+    /*.name =*/ "[email protected]",
+    NULL, // WINSCP
+    /*.main_type =*/ KEXTYPE_ECDH,
+    /*.hash =*/ &ssh_sha512,
+    /*.ecdh_vt =*/ &hybrid_selector_vt,
+    /*.extra =*/ &ssh_ntru_curve25519_hybrid,
 };
 
 static const ssh_kex *const ntru_hybrid_list[] = {
@@ -329,18 +353,19 @@ const ssh_kexes ssh_ntru_hybrid_kex = {
 };
 
 static const hybrid_alg ssh_mlkem768_curve25519_hybrid = {
-    .combining_hash = &ssh_sha256,
-    .pq_alg = &ssh_mlkem768,
-    .classical_alg = &ssh_ec_kex_curve25519,
-    .reformat = reformat_mpint_be_32,
+    /*.combining_hash =*/ &ssh_sha256,
+    /*.pq_alg =*/ &ssh_mlkem768,
+    /*.classical_alg =*/ &ssh_ec_kex_curve25519,
+    /*.reformat =*/ reformat_mpint_be_32,
 };
 
 static const ssh_kex ssh_mlkem768_curve25519 = {
-    .name = "mlkem768x25519-sha256",
-    .main_type = KEXTYPE_ECDH,
-    .hash = &ssh_sha256,
-    .ecdh_vt = &hybrid_selector_vt,
-    .extra = &ssh_mlkem768_curve25519_hybrid,
+    /*.name =*/ "mlkem768x25519-sha256",
+    NULL, // WINSCP
+    /*.main_type =*/ KEXTYPE_ECDH,
+    /*.hash =*/ &ssh_sha256,
+    /*.ecdh_vt =*/ &hybrid_selector_vt,
+    /*.extra =*/ &ssh_mlkem768_curve25519_hybrid,
 };
 
 static const ssh_kex *const mlkem_curve25519_hybrid_list[] = {
@@ -352,33 +377,35 @@ const ssh_kexes ssh_mlkem_curve25519_hybrid_kex = {
 };
 
 static const hybrid_alg ssh_mlkem768_p256_hybrid = {
-    .combining_hash = &ssh_sha256,
-    .pq_alg = &ssh_mlkem768,
-    .classical_alg = &ssh_ec_kex_nistp256,
-    .reformat = reformat_mpint_be_32,
+    /*.combining_hash =*/ &ssh_sha256,
+    /*.pq_alg =*/ &ssh_mlkem768,
+    /*.classical_alg =*/ &ssh_ec_kex_nistp256,
+    /*.reformat =*/ reformat_mpint_be_32,
 };
 
 static const ssh_kex ssh_mlkem768_p256 = {
-    .name = "mlkem768nistp256-sha256",
-    .main_type = KEXTYPE_ECDH,
-    .hash = &ssh_sha256,
-    .ecdh_vt = &hybrid_selector_vt,
-    .extra = &ssh_mlkem768_p256_hybrid,
+    /*.name =*/ "mlkem768nistp256-sha256",
+    NULL, // WINSCP
+    /*.main_type =*/ KEXTYPE_ECDH,
+    /*.hash =*/ &ssh_sha256,
+    /*.ecdh_vt =*/ &hybrid_selector_vt,
+    /*.extra =*/ &ssh_mlkem768_p256_hybrid,
 };
 
 static const hybrid_alg ssh_mlkem1024_p384_hybrid = {
-    .combining_hash = &ssh_sha384,
-    .pq_alg = &ssh_mlkem1024,
-    .classical_alg = &ssh_ec_kex_nistp384,
-    .reformat = reformat_mpint_be_48,
+    /*.combining_hash =*/ &ssh_sha384,
+    /*.pq_alg =*/ &ssh_mlkem1024,
+    /*.classical_alg =*/ &ssh_ec_kex_nistp384,
+    /*.reformat =*/ reformat_mpint_be_48,
 };
 
 static const ssh_kex ssh_mlkem1024_p384 = {
-    .name = "mlkem1024nistp384-sha384",
-    .main_type = KEXTYPE_ECDH,
-    .hash = &ssh_sha384,
-    .ecdh_vt = &hybrid_selector_vt,
-    .extra = &ssh_mlkem1024_p384_hybrid,
+    /*.name =*/ "mlkem1024nistp384-sha384",
+    NULL, // WINSCP
+    /*.main_type =*/ KEXTYPE_ECDH,
+    /*.hash =*/ &ssh_sha384,
+    /*.ecdh_vt =*/ &hybrid_selector_vt,
+    /*.extra =*/ &ssh_mlkem1024_p384_hybrid,
 };
 
 static const ssh_kex *const mlkem_nist_hybrid_list[] = {

+ 148 - 59
source/putty/crypto/mlkem.c

@@ -36,13 +36,13 @@ struct mlkem_params {
  * Specific parameter sets.
  */
 const mlkem_params mlkem_params_512 = {
-    .k = 2, .eta_1 = 3, .eta_2 = 2, .d_u = 10, .d_v = 4,
+    /*.k =*/ 2, /*.eta_1 =*/ 3, /*.eta_2 =*/ 2, /*.d_u =*/ 10, /*.d_v =*/ 4,
 };
 const mlkem_params mlkem_params_768 = {
-    .k = 3, .eta_1 = 2, .eta_2 = 2, .d_u = 10, .d_v = 4,
+    /*.k =*/ 3, /*.eta_1 =*/ 2, /*.eta_2 =*/ 2, /*.d_u =*/ 10, /*.d_v =*/ 4,
 };
 const mlkem_params mlkem_params_1024 = {
-    .k = 4, .eta_1 = 2, .eta_2 = 2, .d_u = 11, .d_v = 5,
+    /*.k =*/ 4, /*.eta_1 =*/ 2, /*.eta_2 =*/ 2, /*.d_u =*/ 11, /*.d_v =*/ 5,
 };
 #define KMAX 4
 
@@ -120,10 +120,13 @@ static void mlkem_ntt(uint16_t *v)
     const uint64_t Qrecip = reciprocal_for_reduction(Q);
     size_t next_power = 1;
 
-    for (size_t len = 128; len >= 2; len /= 2) {
-        for (size_t start = 0; start < 256; start += 2*len) {
+    size_t len; // WINSCP
+    for (len = 128; len >= 2; len /= 2) {
+        size_t start; // WINSCP
+        for (start = 0; start < 256; start += 2*len) {
             uint16_t mult = powers_reversed_order[next_power++];
-            for (size_t j = start; j < start + len; j++) {
+            size_t j; // WINSCP
+            for (j = start; j < start + len; j++) {
                 uint16_t t = reduce(mult * v[j + len], Q, Qrecip);
                 v[j + len] = reduce(v[j] + Q - t, Q, Qrecip);
                 v[j] = reduce(v[j] + t, Q, Qrecip);
@@ -140,10 +143,13 @@ static void mlkem_inverse_ntt(uint16_t *v)
     const uint64_t Qrecip = reciprocal_for_reduction(Q);
     size_t next_power = 127;
 
-    for (size_t len = 2; len <= 128; len *= 2) {
-        for (size_t start = 0; start < 256; start += 2*len) {
+    size_t len; // WINSCP
+    for (len = 2; len <= 128; len *= 2) {
+        size_t start; // WINSCP
+        for (start = 0; start < 256; start += 2*len) {
             uint16_t mult = powers_reversed_order[next_power--];
-            for (size_t j = start; j < start + len; j++) {
+            size_t j; // WINSCP
+            for (j = start; j < start + len; j++) {
                 uint16_t t = v[j];
                 v[j] = reduce(t + v[j + len], Q, Qrecip);
                 v[j + len] = reduce(mult * (v[j + len] + Q - t), Q, Qrecip);
@@ -151,8 +157,11 @@ static void mlkem_inverse_ntt(uint16_t *v)
         }
     }
 
-    for (size_t i = 0; i < 256; i++)
+    { // WINSCP
+    size_t i; // WINSCP
+    for (i = 0; i < 256; i++)
         v[i] = reduce(v[i] * 3303, Q, Qrecip);
+    } // WINSCP
 }
 
 /*
@@ -166,7 +175,8 @@ static void mlkem_multiply_ntts(
 {
     const uint64_t Qrecip = reciprocal_for_reduction(Q);
 
-    for (size_t i = 0; i < 128; i++) {
+    size_t i; // WINSCP
+    for (i = 0; i < 128; i++) {
         uint16_t a0 = a[2*i], a1 = a[2*i+1];
         uint16_t b0 = b[2*i], b1 = b[2*i+1];
         uint16_t mult = powers_odd_reversed_order[i];
@@ -237,6 +247,7 @@ static void mlkem_matrix_alloc(mlkem_matrix_storage *storage, ...)
     va_end(ap);
 
     storage->data = snewn(256 * storage->n, uint16_t);
+    { // WINSCP
     size_t pos = 0;
     va_start(ap, storage);
     while ((m = va_arg(ap, mlkem_matrix *)) != NULL) {
@@ -247,6 +258,7 @@ static void mlkem_matrix_alloc(mlkem_matrix_storage *storage, ...)
         pos += nrows * ncols;
     }
     va_end(ap);
+    } // WINSCP
 }
 
 /* Clear and free the storage allocated by mlkem_matrix_alloc. */
@@ -267,15 +279,20 @@ static void mlkem_matrix_add(mlkem_matrix *out, const mlkem_matrix *left,
     assert(out->nrows == right->nrows);
     assert(out->ncols == right->ncols);
 
-    for (size_t i = 0; i < out->nrows; i++) {
-        for (size_t j = 0; j < out->ncols; j++) {
+    { // WINSCP
+    size_t i; // WINSCP
+    for (i = 0; i < out->nrows; i++) {
+        size_t j; // WINSCP
+        for (j = 0; j < out->ncols; j++) {
             const uint16_t *lv = left->data + 256*(i * left->ncols + j);
             const uint16_t *rv = right->data + 256*(i * right->ncols + j);
             uint16_t *ov = out->data + 256*(i * out->ncols + j);
-            for (size_t p = 0; p < 256; p++)
+            size_t p; // WINSCP
+            for (p = 0; p < 256; p++)
                 ov[p] = reduce(lv[p] + rv[p] , Q, Qrecip);
         }
     }
+    } // WINSCP
 }
 
 /* Subtract matrices. */
@@ -289,28 +306,35 @@ static void mlkem_matrix_sub(mlkem_matrix *out, const mlkem_matrix *left,
     assert(out->nrows == right->nrows);
     assert(out->ncols == right->ncols);
 
-    for (size_t i = 0; i < out->nrows; i++) {
-        for (size_t j = 0; j < out->ncols; j++) {
+    { // WINSCP
+    size_t i; // WINSCP
+    for (i = 0; i < out->nrows; i++) {
+        size_t j; // WINSCP
+        for (j = 0; j < out->ncols; j++) {
             const uint16_t *lv = left->data + 256*(i * left->ncols + j);
             const uint16_t *rv = right->data + 256*(i * right->ncols + j);
             uint16_t *ov = out->data + 256*(i * out->ncols + j);
-            for (size_t p = 0; p < 256; p++)
+            size_t p; // WINSCP
+            for (p = 0; p < 256; p++)
                 ov[p] = reduce(lv[p] + Q - rv[p] , Q, Qrecip);
         }
     }
+    } // WINSCP
 }
 
 /* Convert every element of a matrix into NTT representation. */
 static void mlkem_matrix_ntt(mlkem_matrix *m)
 {
-    for (size_t i = 0; i < m->nrows * m->ncols; i++)
+    size_t i; // WINSCP
+    for (i = 0; i < m->nrows * m->ncols; i++)
         mlkem_ntt(m->data + i * 256);
 }
 
 /* Convert every element of a matrix out of NTT representation. */
 static void mlkem_matrix_inverse_ntt(mlkem_matrix *m)
 {
-    for (size_t i = 0; i < m->nrows * m->ncols; i++)
+    size_t i; // WINSCP
+    for (i = 0; i < m->nrows * m->ncols; i++)
         mlkem_inverse_ntt(m->data + i * 256);
 }
 
@@ -335,25 +359,35 @@ static void mlkem_matrix_mul(mlkem_matrix *out, const mlkem_matrix *left,
     assert(left_ncols == right->nrows);
     assert(right->ncols == out->ncols);
 
+    { // WINSCP
     uint16_t work[256];
 
-    for (size_t i = 0; i < out->nrows; i++) {
-        for (size_t j = 0; j < out->ncols; j++) {
+    size_t i; // WINSCP
+    for (i = 0; i < out->nrows; i++) {
+        size_t j; // WINSCP
+        for (j = 0; j < out->ncols; j++) {
             uint16_t *thisout = out->data + 256 * (i * out->ncols + j);
             memset(thisout, 0, 256 * sizeof(uint16_t));
-            for (size_t k = 0; k < right->nrows; k++) {
+            { // WINSCP
+            size_t k; // WINSCP
+            for (k = 0; k < right->nrows; k++) {
                 size_t left_index = left_transposed ?
                     k * left->ncols + i : i * left->ncols + k;
                 const uint16_t *lv = left->data + 256*left_index;
                 const uint16_t *rv = right->data + 256*(k * right->ncols + j);
                 mlkem_multiply_ntts(work, lv, rv);
-                for (size_t p = 0; p < 256; p++)
+                { // WINSCP
+                size_t p; // WINSCP
+                for (p = 0; p < 256; p++)
                     thisout[p] = reduce(thisout[p] + work[p], Q, Qrecip);
+                } // WINSCP
             }
+            } // WINSCP
         }
     }
 
     smemclr(work, sizeof(work));
+    } // WINSCP
 }
 
 /* ----------------------------------------------------------------------
@@ -374,8 +408,10 @@ static void mlkem_sample_ntt(uint16_t *output, ptrlen seed); /* forward ref */
  */
 static void mlkem_matrix_from_seed(mlkem_matrix *m, const void *rho)
 {
-    for (unsigned r = 0; r < m->nrows; r++) {
-        for (unsigned c = 0; c < m->ncols; c++) {
+    unsigned r; // WINSCP
+    for (r = 0; r < m->nrows; r++) {
+        unsigned c; // WINSCP
+        for (c = 0; c < m->ncols; c++) {
             unsigned char seedbuf[34];
             memcpy(seedbuf, rho, 32);
             seedbuf[32] = c;
@@ -406,10 +442,13 @@ static void mlkem_sample_ntt(uint16_t *output, ptrlen seed)
     unsigned char bytebuf[4];
     bytebuf[3] = '\0';
 
-    for (size_t pos = 0; pos < 256 ;) {
+    { // WINSCP
+    size_t pos; // WINSCP
+    for (pos = 0; pos < 256 ;) {
         /* Read 3 bytes into the low-order end of bytebuf. The fourth
          * byte is always 0, so this gives us a random 24-bit integer. */
         shake_xof_read(sx, &bytebuf, 3);
+        { // WINSCP
         uint32_t random24 = GET_32BIT_LSB_FIRST(bytebuf);
 
         /*
@@ -428,9 +467,11 @@ static void mlkem_sample_ntt(uint16_t *output, ptrlen seed)
             output[pos++] = d1;
         if (d2 < Q && pos < 256)
             output[pos++] = d2;
+        } // WINSCP
     }
 
     shake_xof_free(sx);
+    } // WINSCP
 }
 
 /*
@@ -452,30 +493,39 @@ static void mlkem_matrix_poly_cbd(
     unsigned char seedbuf[33];
     memcpy(seedbuf, sigma, 32);
 
+    { // WINSCP
     unsigned char *randombuf = snewn(eta * 64, unsigned char);
 
-    for (unsigned r = 0; r < v->nrows * v->ncols; r++) {
+    unsigned r; // WINSCP
+    for (r = 0; r < v->nrows * v->ncols; r++) {
         seedbuf[32] = r + offset;
+        { // WINSCP
         ShakeXOF *sx = shake256_xof_from_input(make_ptrlen(seedbuf, 33));
         shake_xof_read(sx, randombuf, eta * 64);
         shake_xof_free(sx);
 
-        for (size_t i = 0; i < 256; i++) {
+        { // WINSCP
+        size_t i; // WINSCP
+        for (i = 0; i < 256; i++) {
             unsigned x = 0, y = 0;
-            for (size_t j = 0; j < eta; j++) {
+            size_t j; // WINSCP
+            for (j = 0; j < eta; j++) {
                 size_t bitpos = 2 * i * eta + j;
                 x += 1 & ((randombuf[bitpos >> 3]) >> (bitpos & 7));
             }
-            for (size_t j = 0; j < eta; j++) {
+            for (j = 0; j < eta; j++) {
                 size_t bitpos = 2 * i * eta + eta + j;
                 y += 1 & ((randombuf[bitpos >> 3]) >> (bitpos & 7));
             }
             v->data[256 * r + i] = reduce(x + Q - y, Q, Qrecip);
         }
+        } // WINSCP
+        } // WINSCP
     }
     smemclr(seedbuf, sizeof(seedbuf));
     smemclr(randombuf, eta * 64);
     sfree(randombuf);
+    } // WINSCP
 }
 
 /* ----------------------------------------------------------------------
@@ -499,7 +549,8 @@ static void mlkem_byte_encode_lossless(
 {
     unsigned char *out = (unsigned char *)outv;
     uint32_t buffer = 0, bufbits = 0;
-    for (size_t i = 0; i < 256*n; i++) {
+    size_t i; // WINSCP
+    for (i = 0; i < 256*n; i++) {
         buffer |= (uint32_t) in[i] << bufbits;
         bufbits += 12;
         while (bufbits >= 8) {
@@ -524,7 +575,8 @@ static bool mlkem_byte_decode_lossless(
 {
     const unsigned char *in = (const unsigned char *)inv;
     uint32_t buffer = 0, bufbits = 0;
-    for (size_t i = 0; i < 384*n; i++) {
+    size_t i; // WINSCP
+    for (i = 0; i < 384*n; i++) {
         buffer |= (uint32_t) in[i] << bufbits;
         bufbits += 8;
         while (bufbits >= 12) {
@@ -557,7 +609,8 @@ static void mlkem_byte_encode_compressed(
 
     unsigned char *out = (unsigned char *)outv;
     uint32_t buffer = 0, bufbits = 0;
-    for (size_t i = 0; i < 256*n; i++) {
+    size_t i; // WINSCP
+    for (i = 0; i < 256*n; i++) {
         uint32_t dividend = ((uint32_t)in[i] << (d+1)) + Q;
         uint32_t quotient;
         reduce_with_quot(dividend, &quotient, 2*Q, Qrecip);
@@ -591,7 +644,8 @@ static void mlkem_byte_decode_compressed(
 {
     const unsigned char *in = (const unsigned char *)inv;
     uint32_t buffer = 0, bufbits = 0;
-    for (size_t i = 0; i < 32*d*n; i++) {
+    size_t i; // WINSCP
+    for (i = 0; i < 32*d*n; i++) {
         buffer |= (uint32_t) in[i] << bufbits;
         bufbits += 8;
         while (bufbits >= d) {
@@ -650,9 +704,11 @@ void mlkem_keygen_rho_sigma(
      * The encryption key is the vector t, plus the random seed rho
      * from which anyone can reconstruct the matrix A.
      */
+    { // WINSCP
     unsigned char ek[1568];
     mlkem_byte_encode_lossless(ek, t->data, params->k);
     memcpy(ek + 384 * params->k, rho, 32);
+    { // WINSCP
     size_t eklen = 384 * params->k + 32;
     put_data(ek_out, ek, eklen);
 
@@ -660,8 +716,10 @@ void mlkem_keygen_rho_sigma(
      * The decryption key (for the internal "K-PKE" public-key system)
      * is the vector s.
      */
+    { // WINSCP
     unsigned char dk[1536];
     mlkem_byte_encode_lossless(dk, s->data, params->k);
+    { // WINSCP
     size_t dklen = 384 * params->k;
 
     /*
@@ -677,8 +735,10 @@ void mlkem_keygen_rho_sigma(
      */
     put_data(dk_out, dk, dklen);
     put_data(dk_out, ek, eklen);
+    { // WINSCP
     ssh_hash *h = ssh_hash_new(&ssh_sha3_256);
     put_data(h, ek, eklen);
+    { // WINSCP
     unsigned char ekhash[32];
     ssh_hash_final(h, ekhash);
     put_data(dk_out, ekhash, 32);
@@ -688,6 +748,12 @@ void mlkem_keygen_rho_sigma(
     smemclr(ek, sizeof(ek));
     smemclr(ekhash, sizeof(ekhash));
     smemclr(dk, sizeof(dk));
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 
 /*
@@ -768,6 +834,7 @@ bool mlkem_encaps_internal(
      * the output shared secret, plus some randomness for making up
      * the vectors below.
      */
+    { // WINSCP
     unsigned char kr[64];
     unsigned char ekhash[32];
     ssh_hash *h;
@@ -780,6 +847,7 @@ bool mlkem_encaps_internal(
     put_data(h, m, 32);
     put_data(h, ekhash, 32);
     ssh_hash_final(h, kr);
+    { // WINSCP
     const unsigned char *k = kr, *r = kr + 32;
 
     /*
@@ -841,6 +909,7 @@ bool mlkem_encaps_internal(
      * The ciphertext consists of u and v, both encoded lossily, with
      * different numbers of bits retained per element.
      */
+    { // WINSCP
     char c[1568];
     mlkem_byte_encode_compressed(c, u->data, params->d_u, params->k);
     mlkem_byte_encode_compressed(c + 32 * params->k * params->d_u,
@@ -857,6 +926,9 @@ bool mlkem_encaps_internal(
     mlkem_matrix_storage_free(storage);
 
     return true;
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 
 /*
@@ -867,9 +939,11 @@ bool mlkem_encaps(BinarySink *ciphertext, BinarySink *kout,
 {
     unsigned char m[32];
     random_read(m, 32);
+    { // WINSCP
     bool success = mlkem_encaps_internal(ciphertext, kout, params, ek, m);
     smemclr(m, sizeof(m));
     return success;
+    } // WINSCP
 }
 
 /*
@@ -890,6 +964,7 @@ bool mlkem_decaps(BinarySink *k_out, const mlkem_params *params,
      * Further validation: extract the encryption key from the middle
      * of dk, hash it, and check the hash matches.
      */
+    { // WINSCP
     const unsigned char *dkp = (const unsigned char *)dk.ptr;
     const unsigned char *cp = (const unsigned char *)c.ptr;
     ptrlen ek = make_ptrlen(dkp + 384*params->k, 384*params->k + 32);
@@ -901,6 +976,7 @@ bool mlkem_decaps(BinarySink *k_out, const mlkem_params *params,
     if (!smemeq(ekhash, dkp + 768*params->k + 32, 32))
         return false;
 
+    { // WINSCP
     mlkem_matrix_storage storage[1];
     mlkem_matrix u[1], v[1], s[1], w[1];
     mlkem_matrix_alloc(storage,
@@ -954,6 +1030,7 @@ bool mlkem_decaps(BinarySink *k_out, const mlkem_params *params,
      * that makes you not worry about spontaneous hash collisions),
      * but it's not actually impossible.
      */
+    { // WINSCP
     unsigned char m[32];
     mlkem_byte_encode_compressed(m, w->data, 1, 1);
 
@@ -967,10 +1044,12 @@ bool mlkem_decaps(BinarySink *k_out, const mlkem_params *params,
      * This is also where we get the output secret k from: the
      * encapsulation function creates it as half of the hash of m.
      */
+    { // WINSCP
     unsigned char c_regen[1568], k[32];
     buffer_sink c_sink[1], k_sink[1];
     buffer_sink_init(c_sink, c_regen, sizeof(c_regen));
     buffer_sink_init(k_sink, k, sizeof(k));
+    { // WINSCP
     bool success = mlkem_encaps_internal(
         BinarySink_UPCAST(c_sink), BinarySink_UPCAST(k_sink), params, ek, m);
     /* If any application of ML-KEM uses a dk given to it by someone
@@ -990,6 +1069,7 @@ bool mlkem_decaps(BinarySink *k_out, const mlkem_params *params,
      * k_reject is that secret; for constant-time reasons we generate
      * it unconditionally.
      */
+    { // WINSCP
     unsigned char k_reject[32];
     h = ssh_hash_new(&ssh_shake256_32bytes);
     put_data(h, dkp + 768 * params->k + 64, 32);
@@ -1000,9 +1080,11 @@ bool mlkem_decaps(BinarySink *k_out, const mlkem_params *params,
      * Now replace k with k_reject if the ciphertexts didn't match.
      */
     assert((void *)c_sink->out == (void *)(c_regen + c.len));
+    { // WINSCP
     unsigned match = smemeq(c.ptr, c_regen, c.len);
     unsigned mask = match - 1;
-    for (size_t i = 0; i < 32; i++)
+    size_t i; // WINSCP
+    for (i = 0; i < 32; i++)
         k[i] ^= mask & (k[i] ^ k_reject[i]);
 
     /*
@@ -1016,6 +1098,13 @@ bool mlkem_decaps(BinarySink *k_out, const mlkem_params *params,
     smemclr(k, sizeof(k));
     smemclr(k_reject, sizeof(k_reject));
     return true;
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 
 /* ----------------------------------------------------------------------
@@ -1057,34 +1146,34 @@ static void mlkem_vt_free_dk(pq_kem_dk *dk)
 }
 
 const pq_kemalg ssh_mlkem512 = {
-    .keygen = mlkem_vt_keygen,
-    .encaps = mlkem_vt_encaps,
-    .decaps = mlkem_vt_decaps,
-    .free_dk = mlkem_vt_free_dk,
-    .extra = &mlkem_params_512,
-    .description = "ML-KEM-512",
-    .ek_len = 384 * 2 + 32,
-    .c_len = 32 * (10 * 2 + 4),
+    /*.keygen =*/ mlkem_vt_keygen,
+    /*.encaps =*/ mlkem_vt_encaps,
+    /*.decaps =*/ mlkem_vt_decaps,
+    /*.free_dk =*/ mlkem_vt_free_dk,
+    /*.extra =*/ &mlkem_params_512,
+    /*.description =*/ "ML-KEM-512",
+    /*.ek_len =*/ 384 * 2 + 32,
+    /*.c_len =*/ 32 * (10 * 2 + 4),
 };
 
 const pq_kemalg ssh_mlkem768 = {
-    .keygen = mlkem_vt_keygen,
-    .encaps = mlkem_vt_encaps,
-    .decaps = mlkem_vt_decaps,
-    .free_dk = mlkem_vt_free_dk,
-    .extra = &mlkem_params_768,
-    .description = "ML-KEM-768",
-    .ek_len = 384 * 3 + 32,
-    .c_len = 32 * (10 * 3 + 4),
+    /*.keygen =*/ mlkem_vt_keygen,
+    /*.encaps =*/ mlkem_vt_encaps,
+    /*.decaps =*/ mlkem_vt_decaps,
+    /*.free_dk =*/ mlkem_vt_free_dk,
+    /*.extra =*/ &mlkem_params_768,
+    /*.description =*/ "ML-KEM-768",
+    /*.ek_len =*/ 384 * 3 + 32,
+    /*.c_len =*/ 32 * (10 * 3 + 4),
 };
 
 const pq_kemalg ssh_mlkem1024 = {
-    .keygen = mlkem_vt_keygen,
-    .encaps = mlkem_vt_encaps,
-    .decaps = mlkem_vt_decaps,
-    .free_dk = mlkem_vt_free_dk,
-    .extra = &mlkem_params_1024,
-    .description = "ML-KEM-1024",
-    .ek_len = 384 * 4 + 32,
-    .c_len = 32 * (11 * 4 + 5),
+    /*.keygen =*/ mlkem_vt_keygen,
+    /*.encaps =*/ mlkem_vt_encaps,
+    /*.decaps =*/ mlkem_vt_decaps,
+    /*.free_dk =*/ mlkem_vt_free_dk,
+    /*.extra =*/ &mlkem_params_1024,
+    /*.description =*/ "ML-KEM-1024",
+    /*.ek_len =*/ 384 * 4 + 32,
+    /*.c_len =*/ 32 * (11 * 4 + 5),
 };

+ 28 - 8
source/putty/crypto/ntru.c

@@ -1641,6 +1641,7 @@ static bool ntru_vt_encaps(const pq_kemalg *alg, BinarySink *c, BinarySink *k,
     BinarySource src[1];
     BinarySource_BARE_INIT_PL(src, ek);
 
+    { // WINSCP
     uint16_t *pubkey = snewn(p_LIVE, uint16_t);
     ntru_decode_pubkey(pubkey, p_LIVE, q_LIVE, src);
 
@@ -1651,14 +1652,17 @@ static bool ntru_vt_encaps(const pq_kemalg *alg, BinarySink *c, BinarySink *k,
     }
 
     /* Invent a valid NTRU plaintext. */
+    { // WINSCP
     uint16_t *plaintext = snewn(p_LIVE, uint16_t);
     ntru_gen_short(plaintext, p_LIVE, w_LIVE);
 
     /* Encrypt the plaintext, and encode the ciphertext into a strbuf,
      * so we can reuse it for both the session hash and sending to the
      * client. */
+    { // WINSCP
     uint16_t *ciphertext = snewn(p_LIVE, uint16_t);
     ntru_encrypt(ciphertext, plaintext, pubkey, p_LIVE, q_LIVE);
+    { // WINSCP
     strbuf *ciphertext_encoded = strbuf_new_nm();
     ntru_encode_ciphertext(ciphertext, p_LIVE, q_LIVE,
                            BinarySink_UPCAST(ciphertext_encoded));
@@ -1666,11 +1670,13 @@ static bool ntru_vt_encaps(const pq_kemalg *alg, BinarySink *c, BinarySink *k,
 
     /* Compute the confirmation hash, and append that to the data sent
      * to the other side. */
+    { // WINSCP
     uint8_t confhash[32];
     ntru_confirmation_hash(confhash, plaintext, pubkey, p_LIVE, q_LIVE);
     put_data(c, confhash, 32);
 
     /* Compute the session hash, i.e. the output shared secret. */
+    { // WINSCP
     uint8_t sesshash[32];
     ntru_session_hash(sesshash, 1, plaintext, p_LIVE,
                       ptrlen_from_strbuf(ciphertext_encoded),
@@ -1685,6 +1691,12 @@ static bool ntru_vt_encaps(const pq_kemalg *alg, BinarySink *c, BinarySink *k,
     smemclr(sesshash, sizeof(sesshash));
 
     return true;
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 
 static bool ntru_vt_decaps(pq_kem_dk *dk, BinarySink *k, ptrlen c)
@@ -1713,19 +1725,23 @@ static bool ntru_vt_decaps(pq_kem_dk *dk, BinarySink *k, ptrlen c)
     ntru_decrypt(plaintext, ciphertext, ndk->keypair);
 
     /* Make the confirmation hash */
+    { // WINSCP
     uint8_t confhash[32];
     ntru_confirmation_hash(confhash, plaintext, ndk->keypair->h,
                            p_LIVE, q_LIVE);
 
     /* Check it matches the one the server sent */
+    { // WINSCP
     unsigned ok = smemeq(confhash, confirmation_hash.ptr, 32);
 
     /* If not, substitute in rho for the plaintext in the session hash */
     unsigned mask = ok-1;
-    for (size_t i = 0; i < p_LIVE; i++)
+    size_t i; // WINSCP
+    for (i = 0; i < p_LIVE; i++)
         plaintext[i] ^= mask & (plaintext[i] ^ ndk->keypair->rho[i]);
 
     /* Compute the session hash, whether or not we did that */
+    { // WINSCP
     uint8_t sesshash[32];
     ntru_session_hash(sesshash, ok, plaintext, p_LIVE, ciphertext_encoded,
                       confirmation_hash);
@@ -1739,6 +1755,9 @@ static bool ntru_vt_decaps(pq_kem_dk *dk, BinarySink *k, ptrlen c)
     return true;
     } // WINSCP
     } // WINSCP
+    } // WINSCP
+    } // WINSCP
+    } // WINSCP
 }
 
 static void ntru_vt_free_dk(pq_kem_dk *dk)
@@ -1750,11 +1769,12 @@ static void ntru_vt_free_dk(pq_kem_dk *dk)
 }
 
 const pq_kemalg ssh_ntru = {
-    .keygen = ntru_vt_keygen,
-    .encaps = ntru_vt_encaps,
-    .decaps = ntru_vt_decaps,
-    .free_dk = ntru_vt_free_dk,
-    .description = "NTRU Prime",
-    .ek_len = 1158,
-    .c_len = 1039,
+    /*.keygen =*/ ntru_vt_keygen,
+    /*.encaps =*/ ntru_vt_encaps,
+    /*.decaps =*/ ntru_vt_decaps,
+    /*.free_dk =*/ ntru_vt_free_dk,
+    NULL, // WINSCP
+    /*.description =*/ "NTRU Prime",
+    /*.ek_len =*/ 1158,
+    /*.c_len =*/ 1039,
 };

+ 5 - 2
source/putty/crypto/sha3.c

@@ -398,13 +398,15 @@ void shake_xof_read(ShakeXOF *sx, void *output_v, size_t size)
         if (sx->pos == 0) {
             /* Copy the 64-bit words from the Keccak state into the
              * output buffer of bytes */
-            for (unsigned y = 0; y < 5; y++)
-                for (unsigned x = 0; x < 5; x++)
+            unsigned y, x; // WINSCP
+            for (y = 0; y < 5; y++)
+                for (x = 0; x < 5; x++)
                     PUT_64BIT_LSB_FIRST(sx->buf + 8 * (5*y+x),
                                         sx->state.A[x][y]);
         }
 
         /* Read a chunk from the byte buffer */
+        { // WINSCP
         size_t this_size = sx->bytes_per_transform - sx->pos;
         if (this_size > size)
             this_size = size;
@@ -418,6 +420,7 @@ void shake_xof_read(ShakeXOF *sx, void *output_v, size_t size)
             keccak_transform(sx->state.A);
             sx->pos = 0;
         }
+        } // WINSCP
     }
 }
 

+ 2 - 0
source/putty/proxy/cproxy.c

@@ -78,6 +78,7 @@ void http_digest_response(BinarySink *bs, ptrlen username, ptrlen password,
 
     enable_dit(); /* just in case main() forgot */
 
+    { // WINSCP
     unsigned char ncbuf[4];
     PUT_32BIT_MSB_FIRST(ncbuf, nonce_count);
 
@@ -198,4 +199,5 @@ void http_digest_response(BinarySink *bs, ptrlen username, ptrlen password,
     } // WINSCP
     } // WINSCP
     } // WINSCP
+    } // WINSCP
 }

+ 1 - 1
source/resource/TextsCore1.rc

@@ -517,7 +517,7 @@ BEGIN
 
   CORE_VARIABLE_STRINGS, "CORE_VARIABLE"
   PUTTY_BASED_ON, "SSH and SCP code based on PuTTY %s"
-  PUTTY_COPYRIGHT, "Copyright © 1997–2024 Simon Tatham"
+  PUTTY_COPYRIGHT, "Copyright © 1997–2025 Simon Tatham"
   PUTTY_URL, "https://www.chiark.greenend.org.uk/~sgtatham/putty/"
   FILEZILLA_BASED_ON2, "FTP code based on FileZilla"
   FILEZILLA_COPYRIGHT2, "Copyright © Tim Kosse"

+ 2 - 0
source/resource/TextsWin.h

@@ -674,6 +674,8 @@
 #define KEX_NAME_RSA            6078
 #define KEX_NAME_ECDH           6079
 #define KEX_NAME_NTRU_HYBRID    6080
+#define KEX_NAME_MLKEM_25519_HYBRID 6081
+#define KEX_NAME_MLKEM_NIST_HYBRID 6082
 #define LOGIN_KEY_WITH_CERTIFICATE 6090
 #define CERTIFICATE_ADDED       6091
 #define SSH_HOST_CA_EDIT        6092

+ 2 - 0
source/resource/TextsWin1.rc

@@ -679,6 +679,8 @@ BEGIN
         KEX_NAME_RSA, "RSA-based key exchange"
         KEX_NAME_ECDH, "ECDH key exchange"
         KEX_NAME_NTRU_HYBRID, "NTRU Prime / Curve25519 hybrid kex"
+        KEX_NAME_MLKEM_25519_HYBRID, "ML-KEM / Curve25519 hybrid kex"
+        KEX_NAME_MLKEM_NIST_HYBRID, "ML-KEM / NIST ECDH hybrid kex"
         LOGIN_KEY_WITH_CERTIFICATE, "**This key contains an OpenSSH certificate.**\nIt is not supposed to be added to OpenSSH authorized_keys file."
         CERTIFICATE_ADDED, "Matching certificate was detected in '%s' and added to the converted key file."
         SSH_HOST_CA_EDIT, "Edit trusted host certificate authority"