From b311b645f9447f7e765b0e418d3f37c32e2702e1 Mon Sep 17 00:00:00 2001
From: liuxin <liuxin264@huawei.com>
Date: Fri, 29 Oct 2021 16:01:57 +0800
Subject: [PATCH] support use sm3 crypt user password

---
 po/zh_CN.po                                   |  5 ++++
 pyanaconda/core/users.py                      |  7 ++++--
 pyanaconda/ui/gui/spokes/root_password.glade  | 15 ++++++++++++
 pyanaconda/ui/gui/spokes/root_password.py     | 15 +++++++++++-
 pyanaconda/ui/gui/spokes/user.glade           | 16 ++++++++++++-
 pyanaconda/ui/gui/spokes/user.py              | 14 ++++++++++-
 .../pyanaconda_tests/crypt_password_test.py   | 23 +++++++++++++++++++
 7 files changed, 90 insertions(+), 5 deletions(-)
 create mode 100644 tests/nosetests/pyanaconda_tests/crypt_password_test.py

diff --git a/po/zh_CN.po b/po/zh_CN.po
index 7ee5511..df9e015 100644
--- a/po/zh_CN.po
+++ b/po/zh_CN.po
@@ -7203,3 +7203,8 @@ msgstr "开始安装到硬盘"
 #~ msgstr[0] ""
 #~ "<b>%(count)d 个磁盘；容量 %(size)s；空闲空间 %(free)s</b> (包括未分区及文"
 #~ "件系统内的部分)"
+
+#: pyanaconda/ui/gui/spokes/root_password.glade:215
+#: pyanaconda/ui/gui/spokes/user.glade:278
+msgid "Use SM3 to encrypt the password"
+msgstr "使用SM3算法加密密码"
diff --git a/pyanaconda/core/users.py b/pyanaconda/core/users.py
index db34444..171a2d4 100644
--- a/pyanaconda/core/users.py
+++ b/pyanaconda/core/users.py
@@ -35,7 +35,7 @@ from pyanaconda.anaconda_loggers import get_module_logger
 log = get_module_logger(__name__)
 
 
-def crypt_password(password):
+def crypt_password(password, algo=None):
     """Crypt a password.
 
     Process a password with appropriate salted one-way algorithm.
@@ -44,7 +44,10 @@ def crypt_password(password):
     :returns: crypted representation of the original password
     :rtype: str
     """
-    cryptpw = crypt.crypt(password, crypt.METHOD_SHA512)
+    crypt_method = crypt.METHOD_SHA512
+    if algo == "sm3":
+        crypt_method = crypt.METHOD_SM3
+    cryptpw = crypt.crypt(password, crypt_method)
     if cryptpw is None:
         exn = PasswordCryptError(algo=crypt.METHOD_SHA512)
         if errorHandler.cb(exn) == ERROR_RAISE:
diff --git a/pyanaconda/ui/gui/spokes/root_password.glade b/pyanaconda/ui/gui/spokes/root_password.glade
index 6892ae3..e8ff524 100644
--- a/pyanaconda/ui/gui/spokes/root_password.glade
+++ b/pyanaconda/ui/gui/spokes/root_password.glade
@@ -210,6 +210,21 @@
                     <property name="position">2</property>
                   </packing>
                 </child>
+                <child>
+                  <object class="GtkCheckButton" id="passwd_sm3">
+                    <property name="label" translatable="yes">Use SM3 to encrypt the password</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="halign">start</property>
+                    <property name="draw_indicator">True</property>
+                    <signal name="clicked" handler="on_sm3_clicked" swapped="no"/>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
                 <child>
                   <placeholder/>
                 </child>
diff --git a/pyanaconda/ui/gui/spokes/root_password.py b/pyanaconda/ui/gui/spokes/root_password.py
index d609453..9e6477e 100644
--- a/pyanaconda/ui/gui/spokes/root_password.py
+++ b/pyanaconda/ui/gui/spokes/root_password.py
@@ -61,6 +61,8 @@ class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler)
         self._services_module = SERVICES.get_proxy()
         self._refresh_running = False
         self._manually_locked = False
+        # sm3 password method
+        self._passwd_method_sm3 = False
 
     def initialize(self):
         NormalSpoke.initialize(self)
@@ -75,6 +77,9 @@ class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler)
         self._root_password_ssh_login_override.set_visible(False)
         self._root_password_ssh_login_override.set_no_show_all(True)
 
+        # sm3 object
+        self._passwd_method_button = self.builder.get_object("passwd_sm3")
+
         # Install the password checks:
         # - Has a password been specified?
         # - If a password has been specified and there is data in the confirm box, do they match?
@@ -197,9 +202,14 @@ class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler)
             self._users_module.ClearRootPassword()
             return
 
+        if self._passwd_method_sm3 is True:
+            algo = "sm3"
+        else:
+            algo = None
+
         # we have a password - set it to kickstart data
 
-        self._users_module.SetCryptedRootPassword(crypt_password(pw))
+        self._users_module.SetCryptedRootPassword(crypt_password(pw, algo))
 
         # clear any placeholders
         self.remove_placeholder_texts()
@@ -337,3 +347,6 @@ class PasswordSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler)
         if not lock.get_active():
             self.password_entry.grab_focus()
             self._manually_locked = True
+
+    def on_sm3_clicked(self, button):
+        self._passwd_method_sm3 = self._passwd_method_button.get_active()
diff --git a/pyanaconda/ui/gui/spokes/user.glade b/pyanaconda/ui/gui/spokes/user.glade
index 69156b1..1cca343 100644
--- a/pyanaconda/ui/gui/spokes/user.glade
+++ b/pyanaconda/ui/gui/spokes/user.glade
@@ -273,6 +273,20 @@
                         <property name="top_attach">3</property>
                       </packing>
                     </child>
+                    <child>
+                      <object class="GtkCheckButton" id="passwd_sm3">
+                        <property name="label" translatable="yes">Use SM3 to encrypt the password</property>
+                        <property name="can_focus">True</property>
+                        <property name="receives_default">False</property>
+                        <property name="halign">start</property>
+                        <property name="draw_indicator">True</property>
+                        <signal name="clicked" handler="on_sm3_clicked" swapped="no"/>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">8</property>
+                      </packing>
+                    </child>
                     <child>
                       <object class="GtkGrid" id="grid2">
                         <property name="visible">True</property>
@@ -295,7 +309,7 @@
                       </object>
                       <packing>
                         <property name="left_attach">1</property>
-                        <property name="top_attach">8</property>
+                        <property name="top_attach">9</property>
                       </packing>
                     </child>
                     <child>
diff --git a/pyanaconda/ui/gui/spokes/user.py b/pyanaconda/ui/gui/spokes/user.py
index 05e01f8..a5d5828 100644
--- a/pyanaconda/ui/gui/spokes/user.py
+++ b/pyanaconda/ui/gui/spokes/user.py
@@ -256,6 +256,8 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
 
         self._users_module = USERS.get_proxy()
         self._password_is_required = True
+        # sm3 password method
+        self._passwd_method_sm3 = False
 
     def initialize(self):
         NormalSpoke.initialize(self)
@@ -289,6 +291,9 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
         self._password_bar = self.builder.get_object("password_bar")
         self._password_label = self.builder.get_object("password_label")
 
+        # sm3 object
+        self._passwd_method_button = self.builder.get_object("passwd_sm3")
+
         # Install the password checks:
         # - Has a password been specified?
         # - If a password has been specified and there is data in the confirm box, do they match?
@@ -463,7 +468,11 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
         if self.password_required:
             if self.password:
                 self.password_kickstarted = False
-                self.user.password = crypt_password(self.password)
+                if self._passwd_method_sm3 is True:
+                    algo = "sm3"
+                else:
+                    algo = None
+                self.user.password = crypt_password(self.password, algo)
                 self.user.is_crypted = True
                 self.remove_placeholder_texts()
 
@@ -688,3 +697,6 @@ class UserSpoke(FirstbootSpokeMixIn, NormalSpoke, GUISpokeInputCheckHandler):
             NormalSpoke.on_back_clicked(self, button)
         else:
             log.info("Return to hub prevented by password checking rules.")
+
+    def on_sm3_clicked(self, button):
+        self._passwd_method_sm3 = self._passwd_method_button.get_active()
diff --git a/tests/nosetests/pyanaconda_tests/crypt_password_test.py b/tests/nosetests/pyanaconda_tests/crypt_password_test.py
new file mode 100644
index 0000000..0ceb16b
--- /dev/null
+++ b/tests/nosetests/pyanaconda_tests/crypt_password_test.py
@@ -0,0 +1,23 @@
+from pyanaconda.core.users import crypt_password
+import unittest
+import crypt
+import os
+
+@unittest.skipIf(os.geteuid() != 0, "user creation must be run as root")
+class CryptPasswordTest(unittest.TestCase):
+    def setUp(self):
+        pass
+
+    def tearDown(self):
+        pass
+
+    def test_crypt_password(self):
+        origin_password = "password"
+        encrypted = crypt_password(origin_password, "sm3")
+        self.assertTrue(encrypted.startswith("$sm3$"))
+
+        encrypted = crypt_password(origin_password)
+        self.assertTrue(encrypted.startswith("$6$"))
+
+if __name__ == '__main__':
+    unittest.main()
-- 
2.27.0

