diff --git a/app/__init__.py b/app/__init__.py index d55208d..2fc6b39 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -12,9 +12,14 @@ class App: drives: List[Drive] = [] + thread = None + selected = None drive_tree = None + + wipe_result = {"result": None, + "err": ""} def __init__(self): set_default_color_theme("dark-blue") @@ -69,7 +74,11 @@ class App: for drive in self.drives: self.drive_tree.insert("", END, values=( drive.index, drive.name, drive.disk_type, self.__human_size(drive.capacity), drive.serial_num)) - self.selected = None + if self.selected is not None: + items = self.drive_tree.get_children("") + for item in items: + if self.drive_tree.item(item)["values"] == self.selected: + self.drive_tree.selection_add(item) def __load_drives(self): @@ -82,7 +91,7 @@ class App: self.drives.append(Drive(disk.Model, disk.Name, disk.InterfaceType, disk.DefaultBlockSize, int( disk.Size), disk.SerialNumber, disk.Index)) elif self.OS_TYPE == "Linux": - from diskinfo import Disk, DiskInfo + from diskinfo import DiskInfo di = DiskInfo() disks = di.get_disk_list(sorting=True) self.drives = [] @@ -137,6 +146,7 @@ class App: def __drive_selected(self, event): for selected_item in self.drive_tree.selection(): item = self.drive_tree.item(selected_item) + self.selected_id = selected_item self.selected = item["values"] def __erase_drive(self): @@ -151,24 +161,28 @@ class App: thread = None self.progressbar = CTkProgressBar(master=self.bottom_frame, orientation="horizontal", width=200) + self.progressbar.set(0) self.progressbar.pack(side=LEFT, padx=6, pady=6) self.progress_label = CTkLabel(self.bottom_frame, text="0 %") self.progress_label.pack(side=LEFT, padx=5, pady=5) for drive in self.drives: if drive.index == self.selected[0]: - thread = drive.erase(self.method_box.get(), progressbar=self.progressbar, progress_label=self.progress_label) - self.__check_thread(thread) + self.thread = drive.erase(self.method_box.get(), progressbar=self.progressbar, progress_label=self.progress_label, result=self.wipe_result) + self.__check_thread() - def __check_thread(self, thread): - if thread.is_alive(): - self.root.after(1000, lambda: self.__check_thread(thread)) + def __check_thread(self): + if self.thread.is_alive(): + self.root.after(1000, lambda: self.__check_thread()) else: self.progress_label.pack_forget() self.progressbar.pack_forget() self.drive_tree.configure(selectmode="browse") self.erase_button.configure(state='active') - CTkMessagebox(title="Успешно", message=f"{self.selected[1]} стёрт!") + if self.wipe_result["result"]: + CTkMessagebox(title="Успешно", message=f"{self.selected[1]} стёрт!") + else: + CTkMessagebox(title="Ошибка", message=f"{self.wipe_result['err']}") def __show_warning(self, text): CTkMessagebox(title="Ошибка", message=text) @@ -185,7 +199,7 @@ class App: self.drive_tree.heading(col, command=lambda: self.__sort(col, not reverse)) def __ask_question(self): - msg = CTkMessagebox(title="Стереть", message=f"Вы уверены, что хотите стереть {self.selected[1]}?", + msg = CTkMessagebox(title="Стереть", message=f"Вы уверены, что хотите стереть {self.selected[1]} с серийным номером {self.selected[4]}?", icon="question", option_1="Отмена", option_2="Нет", option_3="Да") response = msg.get() diff --git a/app/utils_drive.py b/app/utils_drive.py index dc7d822..22effda 100644 --- a/app/utils_drive.py +++ b/app/utils_drive.py @@ -14,16 +14,18 @@ erasing_methods = { "data": ["random"] }, "Гутмана": { - "num_passes": 0, - "data": [] + "num_passes": 35, + "data": [ + "random", "random", "random", "random", b"\x55", b"\xAA", b"\x92\x49\x24", b"\x49\x24\x92", b"\x24\x92\x49", b"\x00", b"\x11", b"\x22", b"\x33", b"\x44", b"\x55", b"\x66", b"\x77", b"\x88", b"\x99", b"\xAA", b"\xBB", b"\xCC", b"\xDD", b"\xEE", b"\xFF ", b"\x92\x49\x24", b"\x49\x24\x92", b"\x24\x92\x49", b"\x6D\xB6\xDB", b"\xB6\xDB\x6D", b"\xDB\x6D\xB6", "random", "random", "random", "random" + ] }, - "3 прохода": { + "DoD Short": { "num_passes": 3, "data": [b"\x00", b"\xff", "random"] }, - "7 проходов": { + "DoD 5220.22M": { "num_passes": 7, - "data": [b"\x00", b"\xff", "random", "random", b"\x00", b"\x01", "random"] + "data": [b"\x00", b"\xff", "random", b"\x00", b"\xff", "random", "random"] } } @@ -38,13 +40,11 @@ def get_random_bytes(size): class Drive(): - index: int = None + index: int path: str - total_sectors: str - - disk_type = None + disk_type: str name: str @@ -52,7 +52,7 @@ class Drive(): capacity: int - serial_num = None + serial_num: str def __init__(self, name, path, disk_type, block_size, capacity, dev_id, index): self.name = name @@ -64,29 +64,34 @@ class Drive(): self.serial_num = dev_id self.index = index - def erase(self, method, progressbar, progress_label) -> threading.Thread: - t = threading.Thread(target=self.__erasing, args=(method, progressbar,progress_label)) + def erase(self, method: str, progressbar, progress_label, result) -> threading.Thread: + t = threading.Thread(target=self.__erasing, args=(method, progressbar,progress_label,result)) t.start() return t - def __erasing(self, method, progressbar, progress_label): + def __erasing(self, method, progressbar, progress_label, result): write_size = self.block_size*32 - with open(self.path, "wb") as drive: - for i in range(erasing_methods[method]["num_passes"]): - drive.seek(0) - prev_percent = 0 - len_write = 0 - progressbar.set(prev_percent) - print(i) - while len_write < self.capacity: - if erasing_methods[method]["data"][i] == "random": - data = get_random_bytes(write_size) - else: - data = erasing_methods[method]["data"][i] * write_size - drive.write(data) - len_write += write_size - cur_percent = len_write/self.capacity - if cur_percent - prev_percent > 0.01: - progressbar.set(cur_percent) - progress_label.configure(text=f"{int(cur_percent*100)} %") - prev_percent = cur_percent + try: + with open(self.path, "wb") as drive: + for i in range(erasing_methods[method]["num_passes"]): + drive.seek(0) + prev_percent = 0 + len_write = 0 + progressbar.set(prev_percent) + while len_write < self.capacity: + if erasing_methods[method]["data"][i] == "random": + data = get_random_bytes(write_size) + else: + data = erasing_methods[method]["data"][i] * write_size + drive.write(data) + len_write += write_size + cur_percent = len_write/self.capacity + if cur_percent - prev_percent > 0.01: + progressbar.set(cur_percent) + progress_label.configure(text=f"{int(cur_percent*100)} %") + prev_percent = cur_percent + result["result"] = True + result["err"] = "" + except Exception as e: + result["result"] = False + result["err"] = e \ No newline at end of file diff --git a/packages.png b/packages.png new file mode 100644 index 0000000..2f73eeb Binary files /dev/null and b/packages.png differ diff --git a/pygost-5.13/AUTHORS b/pygost-5.13/AUTHORS new file mode 100644 index 0000000..f047789 --- /dev/null +++ b/pygost-5.13/AUTHORS @@ -0,0 +1 @@ +* Sergey Matveev diff --git a/pygost-5.13/COPYING b/pygost-5.13/COPYING new file mode 100644 index 0000000..9a2708d --- /dev/null +++ b/pygost-5.13/COPYING @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff --git a/pygost-5.13/FAQ b/pygost-5.13/FAQ new file mode 100644 index 0000000..63e62d0 --- /dev/null +++ b/pygost-5.13/FAQ @@ -0,0 +1,24 @@ +Frequently asked questions +************************** + +My signature is not validated by other implementations. What is wrong? + + Try to reverse it ('sign[::-1]'). Try to swap its halves + ('sign[len(sign)/2:] + sign[:len(sign)/2]'). Try to reverse its + swapped halves too. + + It is GOST: do you expect serialization unification?! + +My signature is *still* not validated by other implementations! + + Try to reverse digest you are signing/verifying ('dgst[::-1]'). + + It is GOST: do you expect serialization unification?! + +Everything above did not help me. Does PyGOST sucks? + + No way! You still have not tried to reverse your binary private + key, public key and swap its halves. + + It is GOST: do you expect serialization unification?! + diff --git a/pygost-5.13/INSTALL b/pygost-5.13/INSTALL new file mode 100644 index 0000000..e94b639 --- /dev/null +++ b/pygost-5.13/INSTALL @@ -0,0 +1,43 @@ +Download +******** + +No additional dependencies except Python 2.7/3.x interpreter are +required. + + Preferable way is to download tarball with the signature: + + $ [fetch|wget] http://www.pygost.cypherpunks.ru/pygost-5.13.tar.zst + $ [fetch|wget] http://www.pygost.cypherpunks.ru/pygost-5.13.tar.zst.{asc,sig} + [verify signature] + $ zstd -d < pygost-5.13.tar.zst | tar xf - + $ cd pygost-5.13 + $ python setup.py install + + You can obtain releases source code prepared tarballs on +. You *have to* verify downloaded +tarballs authenticity to be sure that you retrieved trusted and +untampered software. There are two options: + +OpenPGP (https://www.openpgp.org/) '.asc' signature + Use GNU Privacy Guard (https://www.gnupg.org/) free software + implementation. For the very first time it is necessary to get + signing public key and import it. It is provided here + (PUBKEY-PGP.asc), but you should check alternate resources. + + pub rsa2048/0xE6FD1269CD0C009E 2016-09-13 + F55A 7619 3A0C 323A A031 0E6B E6FD 1269 CD0C 009E + uid PyGOST releases + + $ gpg --auto-key-locate dane --locate-keys pygost at cypherpunks dot ru + $ gpg --auto-key-locate wkd --locate-keys pygost at cypherpunks dot ru + +OpenSSH (https://www.openssh.com/) '.sig' signature + Public key (PUBKEY-SSH.pub) and its OpenPGP signature + (PUBKEY-SSH.pub.asc) made with the key above. Its fingerprint: + 'SHA256:/Z3T/T2sXaaunefAL6tz3ZykHTDYIMh5TLd9Hh9mxlU'. + + $ ssh-keygen -Y verify -f PUBKEY-SSH.pub -I pygost@cypherpunks.ru -n file \ + -s pygost-5.13.tar.zst.sig < pygost-5.13.tar.zst + + You can obtain development source code with 'git clone +git://git.cypherpunks.ru/pygost.git'. diff --git a/pygost-5.13/MANIFEST.in b/pygost-5.13/MANIFEST.in new file mode 100644 index 0000000..f7ccb5c --- /dev/null +++ b/pygost-5.13/MANIFEST.in @@ -0,0 +1,8 @@ +include AUTHORS +include COPYING +include FAQ +include INSTALL +include NEWS +include README +include THANKS +include VERSION diff --git a/pygost-5.13/NEWS b/pygost-5.13/NEWS new file mode 100644 index 0000000..781db0d --- /dev/null +++ b/pygost-5.13/NEWS @@ -0,0 +1,271 @@ +News +**** + +*5.13* + Ability to use masked 34.10 private keys. + +*5.12* + Fixed incorrect digest calculation when using + 'GOST34112012*.update()' method. + +*5.11* + 'gost34112012''s 'update()'/'digest()' methods are streaming now - + they do not store the whole data in memory. + +*5.10* + Added ISO 10126 'pygost.gost3413.(un)pad_iso10126' padding support. + +*5.9* + Fixed 'wrap.wrap_cryptopro', that ignored Sbox for key + diversification. + +*5.8* + Added human-readable name of the curve in 'GOST3410Curve.name'. + +*5.7* + Fixed MGM ignoring of the set tag size. + +*5.6* + Fixed lint errors for previous release. + +*5.5* + More 34.10 curve parameters aliases: + id-tc26-gost-3410-2012-256-paramSetA -> id-tc26-gost-3410-12-256-paramSetA + id-tc26-gost-3410-2012-256-paramSetB -> id-tc26-gost-3410-12-256-paramSetB + id-tc26-gost-3410-2012-256-paramSetC -> id-tc26-gost-3410-12-256-paramSetC + id-tc26-gost-3410-2012-256-paramSetD -> id-tc26-gost-3410-12-256-paramSetD + id-tc26-gost-3410-2012-512-paramSetTest -> id-tc26-gost-3410-12-512-paramSetTest + id-tc26-gost-3410-2012-512-paramSetA -> id-tc26-gost-3410-12-512-paramSetA + id-tc26-gost-3410-2012-512-paramSetB -> id-tc26-gost-3410-12-512-paramSetB + id-tc26-gost-3410-2012-512-paramSetC -> id-tc26-gost-3410-12-512-paramSetC + +*5.4* + 'gost3410.prv_marshal' helper can make private keys that are in + curve's Q field, for better compatibility with some + implementations. + +*5.3* + * More than 4 times speed increase of 'gost34112012'. + * 'asn1schemas/cert-selfsigned-example.py' optionally can issue + CA signed child certificate. + +*5.2* + * 'GOST3410Curve' has '.contains(point)' method for checking if + point is on the curve. + * 'gost3410_vko' functions check if remote peer's public key is + on the curve. + * Small typing stubs fixes. + +*5.1* + Small typing stubs fixes. + +*5.0* + * Backward incompatible removing of misleading and excess 'mode' + keyword argument from all 'gost3410*' related functions. + Point/key sizes are determined by looking at curve's + parameters size. + * 'asn1schemas/cert-selfsigned-example.py' optionally can create + CA certificate. + +*4.9* + * *Fixed* nasty bug with Edwards curves using in 34.10-VKO + functions: curve's cofactor has not been used + * CTR-ACPKM mode of operation + * OMAC-ACPKM-Master moder of operation + * KExp15/KImp15 key export/import functions + * KDF_GOSTR3411_2012_256, KDF_TREE_GOSTR3411_2012_256 + * KEG export key generation function + +*4.8* + MGM AEAD mode for 64 and 128 bit ciphers. + +*4.7* + Removed 'gost28147.addmod' for simplicity. + +*4.6* + Fix invalid 'gost28147.addmod''s behaviour with much bigger values + than the modulo. + +*4.5* + Fixed digest endianness and more RFC4491bis conformance in + 'asn1schemas/cert-selfsigned-example.py' certificate's. + +*4.4* + * 'id-tc26-gost-3410-2012-512-paramSetTest' curve + * Simple FAQ + * More test vectors for 34.10-2012 + * More X.509, PKCS #10 and corresponding ASN.1 helper structures + +*4.3* + Dummy release with fixed 'pygost.__version__'. + +*4.2* + * 'pygost.gost3410.sign' accepts predefined random data used for + k/r generation + * More test vectors for 34.10-2012 + +*4.1* + * PEP-396 compatible module's '__version__' + * Curve parameters aliases: + id-GostR3410-2001-CryptoPro-XchA-ParamSet -> id-GostR3410-2001-CryptoPro-A-ParamSet + id-GostR3410-2001-CryptoPro-XchB-ParamSet -> id-GostR3410-2001-CryptoPro-C-ParamSet + id-tc26-gost-3410-2012-256-paramSetB -> id-GostR3410-2001-CryptoPro-A-ParamSet + id-tc26-gost-3410-2012-256-paramSetC -> id-GostR3410-2001-CryptoPro-B-ParamSet + id-tc26-gost-3410-2012-256-paramSetD -> id-GostR3410-2001-CryptoPro-C-ParamSet + * Forbid any later GNU GPL version autousage (project's licence + now is GNU GPLv3 only) + +*4.0* + * 34.10-2012 TC26 twisted Edwards curve related parameters + * Coordinates conversion from twisted Edwards to Weierstrass + form and vice versa + * More test vectors + * Backward incompatible Sbox and curves parameters renaming, to + comply with OIDs identifying them: + Gost2814789_TestParamSet -> id-Gost28147-89-TestParamSet + Gost28147_CryptoProParamSetA -> id-Gost28147-89-CryptoPro-A-ParamSet + Gost28147_CryptoProParamSetB -> id-Gost28147-89-CryptoPro-B-ParamSet + Gost28147_CryptoProParamSetC -> id-Gost28147-89-CryptoPro-C-ParamSet + Gost28147_CryptoProParamSetD -> id-Gost28147-89-CryptoPro-D-ParamSet + Gost28147_tc26_ParamZ -> id-tc26-gost-28147-param-Z + GostR3411_94_TestParamSet -> id-GostR3411-94-TestParamSet + GostR3411_94_CryptoProParamSet -> id-GostR3411-94-CryptoProParamSet + + GostR3410_2001_TestParamSet -> id-GostR3410-2001-TestParamSet + GostR3410_2001_CryptoPro_A_ParamSet -> id-GostR3410-2001-CryptoPro-A-ParamSet + GostR3410_2001_CryptoPro_B_ParamSet -> id-GostR3410-2001-CryptoPro-B-ParamSet + GostR3410_2001_CryptoPro_C_ParamSet -> id-GostR3410-2001-CryptoPro-C-ParamSet + GostR3410_2001_CryptoPro_XchA_ParamSet -> id-GostR3410-2001-CryptoPro-XchA-ParamSet + GostR3410_2001_CryptoPro_XchB_ParamSet -> id-GostR3410-2001-CryptoPro-XchB-ParamSet + GostR3410_2012_TC26_256_ParamSetA -> id-tc26-gost-3410-2012-256-paramSetA + GostR3410_2012_TC26_ParamSetA -> id-tc26-gost-3410-12-512-paramSetA + GostR3410_2012_TC26_ParamSetB -> id-tc26-gost-3410-12-512-paramSetB + GostR3410_2012_TC26_ParamSetC -> id-tc26-gost-3410-2012-512-paramSetC + * Backward incompatible 'GOST3410Curve' initialization: all + parameters are passed not as big-endian encoded binaries, but + as integers + * Backward incompatible change: 'gost3410.CURVE_PARAMS' is + disappeared. 'gost3410.CURVES' dictionary holds already + initialized 'GOST3410Curve'. Just use + 'CURVES["id-tc26-gost-3410-12-512-paramSetA"]' instead of + 'GOST3410Curve(*CURVE_PARAMS["id-tc26-gost-3410-12-512-paramSetA"])' + +*3.15* + * Licence changed back to GNU GPLv3+. GNU LGPLv3+ licenced + versions are not available anymore + * More ASN.1-based test vectors (PyDERASN + (http://www.pyderasn.cypherpunks.ru/) dependency required) + +*3.14* + Add missing typing stubs related to previous release. + +*3.13* + * Ability to explicitly specify used 28147-89 Sbox in + 'pygost.wrap.*' functions + * Ability to use key meshing in 28147-89 CBC mode + +*3.12* + * Added mode argument to 'pygost.gost3410_vko.kek_34102012256', + because 256-bit private keys can be used with that algorithm + too. + * Fix incorrect degree sanitizing in + 'pygost.gost3410.GOST3410Curve.exp' preventing using of + 'UKM=1' in 'pygost.gost3410_vko.kek_*' functions. + +*3.11* + Fixed PEP247 typing stub with invalid hexdigest method. + +*3.10* + Additional missing 34.11-* typing stubs. + +*3.9* + Add missing 34.11-2012 PBKDF2 typing stub. + +*3.8* + * 34.11-2012 based PBKDF2 function added + * 34.13-2015 does not require double blocksized IVs + +*3.7* + Fixed 34.13-2015 OFB bug with IVs longer than 2 blocks. + +*3.6* + Fixed source files installation during 'setup.py install' + invocation. + +*3.5* + Dummy release: added long description in package metadata. + +*3.4* + * Small mypy stubs related fixes + * Licence changed from GNU GPLv3+ to GNU LGPLv3+ + +*3.3* + * 'GOST3412Kuz' renamed to 'GOST3412Kuznechik' + * 'GOST3412Magma' implements GOST R 34.12-2015 Magma 64-bit + block cipher + +*3.2* + 34.13-2015 block cipher modes of operation implementations. + +*3.1* + Fixed mypy stubs related to PEP247-successors. + +*3.0* + * 'gost3411_94' renamed to 'gost341194' + * 'gost3411_2012' renamed and split to 'gost34112012256', + 'gost34112012512' + * 'GOST34112012' split to 'GOST34112012256', 'GOST34112012512' + * 'gost3410.kek' moved to separate 'gost3410_vko.kek_34102001' + * VKO GOST R 34.10-2012 appeared in 'gost3410_vko', with test + vectors + * 34.11-94 digest is reversed, to be compatible with HMAC and + PBKDF2 test vectors describe in TC26 documents + * 34.11-94 PBKDF2 test vectors added + * 'gost3410.prv_unmarshal', 'gost3410.pub_marshal', + 'gost3410.pub_unmarshal' helpers added, removing the need of + 'x509' module at all + * 'gost3410.verify' requires '(pubX, pubY)' tuple, instead of + two separate 'pubX', 'pubY' arguments + * 34.11-94 based PBKDF2 function added + +*2.4* + Fixed 34.13 mypy stub. + +*2.3* + Typo and pylint fixes. + +*2.2* + GOST R 34.13-2015 padding methods. + +*2.1* + Documentation and supplementary files refactoring. + +*2.0* + PEP-0247 compatible hashers and MAC. + +*1.0* + * Ability to specify curve in pygost.x509 module + * Ability to use 34.10-2012 in pygost.x509 functions + * Renamed classes and modules: + pygost.gost3410.SIZE_34100 -> pygost.gost3410.SIZE_3410_2001 + pygost.gost3410.SIZE_34112 -> pygost.gost3410.SIZE_3410_2012 + pygost.gost3411_12.GOST341112 -> pygost.gost3411_2012.GOST34112012 + +*0.16* + 34.10-2012 TC26 curve parameters. + +*0.15* + PEP-0484 static typing hints. + +*0.14* + 34.10-2012 workability fix. + +*0.13* + Python3 compatibility. + +*0.11* + GOST R 34.12-2015 Кузнечик (Kuznechik) implementation. + +*0.10* + CryptoPro and GOST key wrapping, CryptoPro key meshing. + diff --git a/pygost-5.13/PKG-INFO b/pygost-5.13/PKG-INFO new file mode 100644 index 0000000..601c6f7 --- /dev/null +++ b/pygost-5.13/PKG-INFO @@ -0,0 +1,96 @@ +Metadata-Version: 2.1 +Name: pygost +Version: 5.13 +Summary: Pure Python GOST cryptographic functions library +Home-page: http://www.pygost.cypherpunks.ru/ +Author: Sergey Matveev +Author-email: stargrave@stargrave.org +License: GPLv3 +Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3) +Classifier: Natural Language :: English +Classifier: Operating System :: OS Independent +Classifier: Programming Language :: Python :: 2 +Classifier: Programming Language :: Python :: 3 +Classifier: Topic :: Security :: Cryptography +Classifier: Topic :: Software Development :: Libraries :: Python Modules +License-File: COPYING +License-File: AUTHORS + +Pure Python 2.7/3.x GOST cryptographic functions library. + +GOST is GOvernment STandard of Russian Federation (and Soviet Union). + +* GOST 28147-89 (RFC 5830) block cipher with ECB, CNT (CTR), CFB, MAC, + CBC (RFC 4357) modes of operation +* various 28147-89-related S-boxes included +* GOST R 34.11-94 hash function (RFC 5831) +* GOST R 34.11-94 based PBKDF2 function +* GOST R 34.11-2012 Стрибог (Streebog) hash function (RFC 6986) +* GOST R 34.11-2012 based PBKDF2 function (Р 50.1.111-2016) +* GOST R 34.10-2001 (RFC 5832) public key signature function +* GOST R 34.10-2012 (RFC 7091) public key signature function +* various 34.10 curve parameters included +* Coordinates conversion from twisted Edwards to Weierstrass form and + vice versa +* VKO GOST R 34.10-2001 key agreement function (RFC 4357) +* VKO GOST R 34.10-2012 key agreement function (RFC 7836) +* 28147-89 and CryptoPro key wrapping (RFC 4357) +* 28147-89 CryptoPro key meshing for CFB and CBC modes (RFC 4357) +* RFC 4491 (using GOST algorithms with X.509) compatibility helpers +* GOST R 34.12-2015 128-bit block cipher Кузнечик (Kuznechik) (RFC 7801) +* GOST R 34.12-2015 64-bit block cipher Магма (Magma) +* GOST R 34.13-2015 padding methods and block cipher modes of operation + (ECB, CTR, OFB, CBC, CFB, MAC), ISO 10126 padding +* MGM AEAD mode for 64 and 128 bit ciphers (RFC 9058) +* CTR-ACPKM, OMAC-ACPKM-Master modes of operation (Р 1323565.1.017-2018) +* KExp15/KImp15 key export/import functions (Р 1323565.1.017-2018) +* KDF_GOSTR3411_2012_256, KDF_TREE_GOSTR3411_2012_256 (Р 50.1.113-2016) +* KEG export key generation function (Р 1323565.1.020-2018) +* PEP247-compatible hash/MAC functions + +Known problems: low performance and non time-constant calculations. + +Example 34.10-2012 keypair generation, signing and verifying: + + >>> from pygost.gost3410 import CURVES + >>> curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + >>> from os import urandom + >>> prv_raw = urandom(64) + >>> from pygost.gost3410 import prv_unmarshal + >>> from pygost.gost3410 import prv_marshal + >>> prv = prv_unmarshal(prv_raw) + >>> prv_raw = prv_marshal(curve, prv) + >>> from pygost.gost3410 import public_key + >>> pub = public_key(curve, prv) + >>> from pygost.gost3410 import pub_marshal + >>> from pygost.utils import hexenc + >>> print "Public key is:", hexenc(pub_marshal(pub)) + >>> from pygost import gost34112012512 + >>> data_for_signing = b"some data" + >>> dgst = gost34112012512.new(data_for_signing).digest() + >>> from pygost.gost3410 import sign + >>> signature = sign(curve, prv, dgst) + >>> from pygost.gost3410 import verify + >>> verify(curve, pub, dgst, signature) + True + +Other examples can be found in docstrings and unittests. +Example self-signed X.509 certificate creation can be found in +pygost/asn1schemas/cert-selfsigned-example.py. + +PyGOST is free software: see the file COPYING for copying conditions. + +PyGOST'es home page is: http://www.pygost.cypherpunks.ru/ +You can read about GOST algorithms more: http://www.gost.cypherpunks.ru/ + +Please send questions, bug reports and patches to +http://lists.cypherpunks.ru/gost.html mailing list. +Announcements also go to this mailing list. + +Development Git source code repository currently is located here: +http://www.git.cypherpunks.ru/?p=pygost.git;a=summary + + diff --git a/pygost-5.13/README b/pygost-5.13/README new file mode 100644 index 0000000..2034715 --- /dev/null +++ b/pygost-5.13/README @@ -0,0 +1,73 @@ +Pure Python 2.7/3.x GOST cryptographic functions library. + +GOST is GOvernment STandard of Russian Federation (and Soviet Union). + +* GOST 28147-89 (RFC 5830) block cipher with ECB, CNT (CTR), CFB, MAC, + CBC (RFC 4357) modes of operation +* various 28147-89-related S-boxes included +* GOST R 34.11-94 hash function (RFC 5831) +* GOST R 34.11-94 based PBKDF2 function +* GOST R 34.11-2012 Стрибог (Streebog) hash function (RFC 6986) +* GOST R 34.11-2012 based PBKDF2 function (Р 50.1.111-2016) +* GOST R 34.10-2001 (RFC 5832) public key signature function +* GOST R 34.10-2012 (RFC 7091) public key signature function +* various 34.10 curve parameters included +* Coordinates conversion from twisted Edwards to Weierstrass form and + vice versa +* VKO GOST R 34.10-2001 key agreement function (RFC 4357) +* VKO GOST R 34.10-2012 key agreement function (RFC 7836) +* 28147-89 and CryptoPro key wrapping (RFC 4357) +* 28147-89 CryptoPro key meshing for CFB and CBC modes (RFC 4357) +* RFC 4491 (using GOST algorithms with X.509) compatibility helpers +* GOST R 34.12-2015 128-bit block cipher Кузнечик (Kuznechik) (RFC 7801) +* GOST R 34.12-2015 64-bit block cipher Магма (Magma) +* GOST R 34.13-2015 padding methods and block cipher modes of operation + (ECB, CTR, OFB, CBC, CFB, MAC), ISO 10126 padding +* MGM AEAD mode for 64 and 128 bit ciphers (RFC 9058) +* CTR-ACPKM, OMAC-ACPKM-Master modes of operation (Р 1323565.1.017-2018) +* KExp15/KImp15 key export/import functions (Р 1323565.1.017-2018) +* KDF_GOSTR3411_2012_256, KDF_TREE_GOSTR3411_2012_256 (Р 50.1.113-2016) +* KEG export key generation function (Р 1323565.1.020-2018) +* PEP247-compatible hash/MAC functions + +Known problems: low performance and non time-constant calculations. + +Example 34.10-2012 keypair generation, signing and verifying: + + >>> from pygost.gost3410 import CURVES + >>> curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + >>> from os import urandom + >>> prv_raw = urandom(64) + >>> from pygost.gost3410 import prv_unmarshal + >>> from pygost.gost3410 import prv_marshal + >>> prv = prv_unmarshal(prv_raw) + >>> prv_raw = prv_marshal(curve, prv) + >>> from pygost.gost3410 import public_key + >>> pub = public_key(curve, prv) + >>> from pygost.gost3410 import pub_marshal + >>> from pygost.utils import hexenc + >>> print "Public key is:", hexenc(pub_marshal(pub)) + >>> from pygost import gost34112012512 + >>> data_for_signing = b"some data" + >>> dgst = gost34112012512.new(data_for_signing).digest() + >>> from pygost.gost3410 import sign + >>> signature = sign(curve, prv, dgst) + >>> from pygost.gost3410 import verify + >>> verify(curve, pub, dgst, signature) + True + +Other examples can be found in docstrings and unittests. +Example self-signed X.509 certificate creation can be found in +pygost/asn1schemas/cert-selfsigned-example.py. + +PyGOST is free software: see the file COPYING for copying conditions. + +PyGOST'es home page is: http://www.pygost.cypherpunks.ru/ +You can read about GOST algorithms more: http://www.gost.cypherpunks.ru/ + +Please send questions, bug reports and patches to +http://lists.cypherpunks.ru/gost.html mailing list. +Announcements also go to this mailing list. + +Development Git source code repository currently is located here: +http://www.git.cypherpunks.ru/?p=pygost.git;a=summary diff --git a/pygost-5.13/THANKS b/pygost-5.13/THANKS new file mode 100644 index 0000000..cb2ec9d --- /dev/null +++ b/pygost-5.13/THANKS @@ -0,0 +1,8 @@ +There are people deserving to be thanked for helping this project: + +* Dmitry Eremin-Solenikov for his + suggestions of TK26 standards usage as a base point for serialized + structures representation +* Alexander Lodin for finding bug in 34.13-2015 + OFB mode with IVs longer than 2 blocks +* Efimov Vasiliy for adding and testing of CBC-mode key meshing diff --git a/pygost-5.13/VERSION b/pygost-5.13/VERSION new file mode 100644 index 0000000..3fe7217 --- /dev/null +++ b/pygost-5.13/VERSION @@ -0,0 +1 @@ +5.13 diff --git a/pygost-5.13/build/lib/pygost/__init__.py b/pygost-5.13/build/lib/pygost/__init__.py new file mode 100644 index 0000000..fba7932 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/__init__.py @@ -0,0 +1,6 @@ +"""Pure Python GOST cryptographic functions library. + +PyGOST is free software: see the file COPYING for copying conditions. +""" + +__version__ = "5.13" diff --git a/pygost-5.13/build/lib/pygost/asn1schemas/__init__.py b/pygost-5.13/build/lib/pygost/asn1schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pygost-5.13/build/lib/pygost/asn1schemas/cert-dane-hash.py b/pygost-5.13/build/lib/pygost/asn1schemas/cert-dane-hash.py new file mode 100644 index 0000000..0292b9e --- /dev/null +++ b/pygost-5.13/build/lib/pygost/asn1schemas/cert-dane-hash.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +"""DANE's SPKI hash calculator +""" + +from base64 import standard_b64decode +from hashlib import sha256 +import sys + +from pygost.asn1schemas.x509 import Certificate + + +lines = sys.stdin.read().split("-----") +idx = lines.index("BEGIN CERTIFICATE") +if idx == -1: + raise ValueError("PEM has no CERTIFICATE") +cert_raw = standard_b64decode(lines[idx + 1]) +cert = Certificate().decod(cert_raw) +print(sha256(cert["tbsCertificate"]["subjectPublicKeyInfo"].encode()).hexdigest()) diff --git a/pygost-5.13/build/lib/pygost/asn1schemas/cert-selfsigned-example.py b/pygost-5.13/build/lib/pygost/asn1schemas/cert-selfsigned-example.py new file mode 100644 index 0000000..bd562b1 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/asn1schemas/cert-selfsigned-example.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +"""Create example self-signed X.509 certificate +""" + +from argparse import ArgumentParser +from base64 import standard_b64decode +from base64 import standard_b64encode +from datetime import datetime +from datetime import timedelta +from os import urandom +from sys import exit as sys_exit +from sys import stdout +from textwrap import fill + +from pyderasn import Any +from pyderasn import BitString +from pyderasn import Boolean +from pyderasn import IA5String +from pyderasn import Integer +from pyderasn import OctetString +from pyderasn import PrintableString +from pyderasn import UTCTime + +from pygost.asn1schemas.oids import id_at_commonName +from pygost.asn1schemas.oids import id_at_countryName +from pygost.asn1schemas.oids import id_ce_authorityKeyIdentifier +from pygost.asn1schemas.oids import id_ce_basicConstraints +from pygost.asn1schemas.oids import id_ce_keyUsage +from pygost.asn1schemas.oids import id_ce_subjectAltName +from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetB +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetC +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetD +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetB +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetC +from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256 +from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512 +from pygost.asn1schemas.prvkey import PrivateKey +from pygost.asn1schemas.prvkey import PrivateKeyAlgorithmIdentifier +from pygost.asn1schemas.prvkey import PrivateKeyInfo +from pygost.asn1schemas.x509 import AlgorithmIdentifier +from pygost.asn1schemas.x509 import AttributeType +from pygost.asn1schemas.x509 import AttributeTypeAndValue +from pygost.asn1schemas.x509 import AttributeValue +from pygost.asn1schemas.x509 import AuthorityKeyIdentifier +from pygost.asn1schemas.x509 import BasicConstraints +from pygost.asn1schemas.x509 import Certificate +from pygost.asn1schemas.x509 import CertificateSerialNumber +from pygost.asn1schemas.x509 import Extension +from pygost.asn1schemas.x509 import Extensions +from pygost.asn1schemas.x509 import GeneralName +from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters +from pygost.asn1schemas.x509 import KeyIdentifier +from pygost.asn1schemas.x509 import KeyUsage +from pygost.asn1schemas.x509 import Name +from pygost.asn1schemas.x509 import RDNSequence +from pygost.asn1schemas.x509 import RelativeDistinguishedName +from pygost.asn1schemas.x509 import SubjectAltName +from pygost.asn1schemas.x509 import SubjectKeyIdentifier +from pygost.asn1schemas.x509 import SubjectPublicKeyInfo +from pygost.asn1schemas.x509 import TBSCertificate +from pygost.asn1schemas.x509 import Time +from pygost.asn1schemas.x509 import Validity +from pygost.asn1schemas.x509 import Version +from pygost.gost3410 import CURVES +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import pub_marshal +from pygost.gost3410 import public_key +from pygost.gost3410 import sign +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.utils import bytes2long + +parser = ArgumentParser(description="Self-signed X.509 certificate creator") +parser.add_argument( + "--ca", + action="store_true", + help="Enable BasicConstraints.cA", +) +parser.add_argument( + "--cn", + required=True, + help="Subject's CommonName", +) +parser.add_argument( + "--country", + help="Subject's Country", +) +parser.add_argument( + "--serial", + help="Serial number", +) +parser.add_argument( + "--ai", + required=True, + help="Signing algorithm: {256[ABCD],512[ABC]}", +) +parser.add_argument( + "--issue-with", + help="Path to PEM with CA to issue the child", +) +parser.add_argument( + "--reuse-key", + help="Path to PEM with the key to reuse", +) +parser.add_argument( + "--out-key", + help="Path to PEM with the resulting key", +) +parser.add_argument( + "--only-key", + action="store_true", + help="Only generate the key", +) +parser.add_argument( + "--out-cert", + help="Path to PEM with the resulting certificate", +) +args = parser.parse_args() +AIs = { + "256A": { + "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetA, + "key_algorithm": id_tc26_gost3410_2012_256, + "prv_len": 32, + "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetA"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256, + "hasher": GOST34112012256, + }, + "256B": { + "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetB, + "key_algorithm": id_tc26_gost3410_2012_256, + "prv_len": 32, + "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetB"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256, + "hasher": GOST34112012256, + }, + "256C": { + "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetC, + "key_algorithm": id_tc26_gost3410_2012_256, + "prv_len": 32, + "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetC"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256, + "hasher": GOST34112012256, + }, + "256D": { + "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetD, + "key_algorithm": id_tc26_gost3410_2012_256, + "prv_len": 32, + "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetD"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256, + "hasher": GOST34112012256, + }, + "512A": { + "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetA, + "key_algorithm": id_tc26_gost3410_2012_512, + "prv_len": 64, + "curve": CURVES["id-tc26-gost-3410-12-512-paramSetA"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512, + "hasher": GOST34112012512, + }, + "512B": { + "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetB, + "key_algorithm": id_tc26_gost3410_2012_512, + "prv_len": 64, + "curve": CURVES["id-tc26-gost-3410-12-512-paramSetB"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512, + "hasher": GOST34112012512, + }, + "512C": { + "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetC, + "key_algorithm": id_tc26_gost3410_2012_512, + "prv_len": 64, + "curve": CURVES["id-tc26-gost-3410-2012-512-paramSetC"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512, + "hasher": GOST34112012512, + }, +} +ai = AIs[args.ai] + +ca_prv = None +ca_cert = None +ca_subj = None +ca_ai = None +if args.issue_with is not None: + with open(args.issue_with, "rb") as fd: + lines = fd.read().decode("ascii").split("-----") + idx = lines.index("BEGIN PRIVATE KEY") + if idx == -1: + raise ValueError("PEM has no PRIVATE KEY") + prv_raw = standard_b64decode(lines[idx + 1]) + idx = lines.index("BEGIN CERTIFICATE") + if idx == -1: + raise ValueError("PEM has no CERTIFICATE") + cert_raw = standard_b64decode(lines[idx + 1]) + pki = PrivateKeyInfo().decod(prv_raw) + ca_prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"])))) + ca_cert = Certificate().decod(cert_raw) + tbs = ca_cert["tbsCertificate"] + ca_subj = tbs["subject"] + curve_oid = GostR34102012PublicKeyParameters().decod(bytes( + tbs["subjectPublicKeyInfo"]["algorithm"]["parameters"] + ))["publicKeyParamSet"] + ca_ai = next(iter([ + params for params in AIs.values() + if params["publicKeyParamSet"] == curve_oid + ])) + +key_params = GostR34102012PublicKeyParameters(( + ("publicKeyParamSet", ai["publicKeyParamSet"]), +)) + + +def pem(obj): + return fill(standard_b64encode(obj.encode()).decode("ascii"), 64) + + +if args.reuse_key is not None: + with open(args.reuse_key, "rb") as fd: + lines = fd.read().decode("ascii").split("-----") + idx = lines.index("BEGIN PRIVATE KEY") + if idx == -1: + raise ValueError("PEM has no PRIVATE KEY") + prv_raw = standard_b64decode(lines[idx + 1]) + pki = PrivateKeyInfo().decod(prv_raw) + prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"])))) +else: + prv_raw = urandom(ai["prv_len"]) + out = stdout if args.out_key is None else open(args.out_key, "w") + print("-----BEGIN PRIVATE KEY-----", file=out) + print(pem(PrivateKeyInfo(( + ("version", Integer(0)), + ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier(( + ("algorithm", ai["key_algorithm"]), + ("parameters", Any(key_params)), + ))), + ("privateKey", PrivateKey(OctetString(prv_raw).encode())), + ))), file=out) + print("-----END PRIVATE KEY-----", file=out) + if args.only_key: + sys_exit() + prv = prv_unmarshal(prv_raw) + +curve = ai["curve"] +pub_raw = pub_marshal(public_key(curve, prv)) +rdn = [RelativeDistinguishedName(( + AttributeTypeAndValue(( + ("type", AttributeType(id_at_commonName)), + ("value", AttributeValue(PrintableString(args.cn))), + )), +))] +if args.country: + rdn.append(RelativeDistinguishedName(( + AttributeTypeAndValue(( + ("type", AttributeType(id_at_countryName)), + ("value", AttributeValue(PrintableString(args.country))), + )), + ))) +subj = Name(("rdnSequence", RDNSequence(rdn))) +not_before = datetime.utcnow() +not_after = not_before + timedelta(days=365 * (10 if args.ca else 1)) +ai_sign = AlgorithmIdentifier(( + ("algorithm", (ai if ca_ai is None else ca_ai)["sign_algorithm"]), +)) +exts = [ + Extension(( + ("extnID", id_ce_subjectKeyIdentifier), + ("extnValue", OctetString( + SubjectKeyIdentifier(GOST34112012256(pub_raw).digest()[:20]).encode() + )), + )), + Extension(( + ("extnID", id_ce_keyUsage), + ("critical", Boolean(True)), + ("extnValue", OctetString(KeyUsage( + ("keyCertSign" if args.ca else "digitalSignature",), + ).encode())), + )), +] +if args.ca: + exts.append(Extension(( + ("extnID", id_ce_basicConstraints), + ("critical", Boolean(True)), + ("extnValue", OctetString(BasicConstraints(( + ("cA", Boolean(True)), + )).encode())), + ))) +else: + exts.append(Extension(( + ("extnID", id_ce_subjectAltName), + ("extnValue", OctetString( + SubjectAltName(( + GeneralName(("dNSName", IA5String(args.cn))), + )).encode() + )), + ))) +if ca_ai is not None: + caKeyId = [ + bytes(SubjectKeyIdentifier().decod(bytes(ext["extnValue"]))) + for ext in ca_cert["tbsCertificate"]["extensions"] + if ext["extnID"] == id_ce_subjectKeyIdentifier + ][0] + exts.append(Extension(( + ("extnID", id_ce_authorityKeyIdentifier), + ("extnValue", OctetString(AuthorityKeyIdentifier(( + ("keyIdentifier", KeyIdentifier(caKeyId)), + )).encode())), + ))) + +serial = ( + bytes2long(GOST34112012256(urandom(16)).digest()[:20]) + if args.serial is None else int(args.serial) +) +tbs = TBSCertificate(( + ("version", Version("v3")), + ("serialNumber", CertificateSerialNumber(serial)), + ("signature", ai_sign), + ("issuer", subj if ca_ai is None else ca_subj), + ("validity", Validity(( + ("notBefore", Time(("utcTime", UTCTime(not_before)))), + ("notAfter", Time(("utcTime", UTCTime(not_after)))), + ))), + ("subject", subj), + ("subjectPublicKeyInfo", SubjectPublicKeyInfo(( + ("algorithm", AlgorithmIdentifier(( + ("algorithm", ai["key_algorithm"]), + ("parameters", Any(key_params)), + ))), + ("subjectPublicKey", BitString(OctetString(pub_raw).encode())), + ))), + ("extensions", Extensions(exts)), +)) +cert = Certificate(( + ("tbsCertificate", tbs), + ("signatureAlgorithm", ai_sign), + ("signatureValue", BitString( + sign(curve, prv, ai["hasher"](tbs.encode()).digest()[::-1]) + if ca_ai is None else + sign(ca_ai["curve"], ca_prv, ca_ai["hasher"](tbs.encode()).digest()[::-1]) + )), +)) +out = stdout if args.out_cert is None else open(args.out_cert, "w") +print("-----BEGIN CERTIFICATE-----", file=out) +print(pem(cert), file=out) +print("-----END CERTIFICATE-----", file=out) diff --git a/pygost-5.13/build/lib/pygost/asn1schemas/cms.py b/pygost-5.13/build/lib/pygost/asn1schemas/cms.py new file mode 100644 index 0000000..8028d2b --- /dev/null +++ b/pygost-5.13/build/lib/pygost/asn1schemas/cms.py @@ -0,0 +1,431 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""CMS related structures (**NOT COMPLETE**) +""" + +from pyderasn import Any +from pyderasn import BitString +from pyderasn import Choice +from pyderasn import Integer +from pyderasn import ObjectIdentifier +from pyderasn import OctetString +from pyderasn import Sequence +from pyderasn import SequenceOf +from pyderasn import SetOf +from pyderasn import tag_ctxc +from pyderasn import tag_ctxp + +from pygost.asn1schemas.oids import id_cms_mac_attr +from pygost.asn1schemas.oids import id_contentType +from pygost.asn1schemas.oids import id_digestedData +from pygost.asn1schemas.oids import id_encryptedData +from pygost.asn1schemas.oids import id_envelopedData +from pygost.asn1schemas.oids import id_Gost28147_89 +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm_omac +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_wrap_kexp15 +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm_omac +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_wrap_kexp15 +from pygost.asn1schemas.oids import id_messageDigest +from pygost.asn1schemas.oids import id_signedData +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 +from pygost.asn1schemas.x509 import AlgorithmIdentifier +from pygost.asn1schemas.x509 import Certificate +from pygost.asn1schemas.x509 import CertificateSerialNumber +from pygost.asn1schemas.x509 import Name +from pygost.asn1schemas.x509 import SubjectPublicKeyInfo + + +class CMSVersion(Integer): + pass + + +class ContentType(ObjectIdentifier): + pass + + +class IssuerAndSerialNumber(Sequence): + schema = ( + ("issuer", Name()), + ("serialNumber", CertificateSerialNumber()), + ) + + +class KeyIdentifier(OctetString): + pass + + +class SubjectKeyIdentifier(KeyIdentifier): + pass + + +class RecipientIdentifier(Choice): + schema = ( + ("issuerAndSerialNumber", IssuerAndSerialNumber()), + ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))), + ) + + +class Gost2814789Key(OctetString): + bounds = (32, 32) + + +class Gost2814789MAC(OctetString): + bounds = (4, 4) + + +class Gost2814789EncryptedKey(Sequence): + schema = ( + ("encryptedKey", Gost2814789Key()), + ("maskKey", Gost2814789Key(impl=tag_ctxp(0), optional=True)), + ("macKey", Gost2814789MAC()), + ) + + +class GostR34102001TransportParameters(Sequence): + schema = ( + ("encryptionParamSet", ObjectIdentifier()), + ("ephemeralPublicKey", SubjectPublicKeyInfo( + impl=tag_ctxc(0), + optional=True, + )), + ("ukm", OctetString()), + ) + + +class GostR3410KeyTransport(Sequence): + schema = ( + ("sessionEncryptedKey", Gost2814789EncryptedKey()), + ("transportParameters", GostR34102001TransportParameters( + impl=tag_ctxc(0), + optional=True, + )), + ) + + +class GostR3410KeyTransport2019(Sequence): + schema = ( + ("encryptedKey", OctetString()), + ("ephemeralPublicKey", SubjectPublicKeyInfo()), + ("ukm", OctetString()), + ) + + +class GostR341012KEGParameters(Sequence): + schema = ( + ("algorithm", ObjectIdentifier()), + ) + + +class KeyEncryptionAlgorithmIdentifier(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), { + id_gostr3412_2015_magma_wrap_kexp15: GostR341012KEGParameters(), + id_gostr3412_2015_kuznyechik_wrap_kexp15: GostR341012KEGParameters(), + }), + (("..", "encryptedKey"), { + id_tc26_gost3410_2012_256: GostR3410KeyTransport(), + id_tc26_gost3410_2012_512: GostR3410KeyTransport(), + id_gostr3412_2015_magma_wrap_kexp15: GostR3410KeyTransport2019(), + id_gostr3412_2015_kuznyechik_wrap_kexp15: GostR3410KeyTransport2019(), + }), + (("..", "recipientEncryptedKeys", any, "encryptedKey"), { + id_tc26_gost3410_2012_256: Gost2814789EncryptedKey(), + id_tc26_gost3410_2012_512: Gost2814789EncryptedKey(), + }), + ))), + ("parameters", Any(optional=True)), + ) + + +class EncryptedKey(OctetString): + pass + + +class KeyTransRecipientInfo(Sequence): + schema = ( + ("version", CMSVersion()), + ("rid", RecipientIdentifier()), + ("keyEncryptionAlgorithm", KeyEncryptionAlgorithmIdentifier()), + ("encryptedKey", EncryptedKey()), + ) + + +class OriginatorPublicKey(Sequence): + schema = ( + ("algorithm", AlgorithmIdentifier()), + ("publicKey", BitString()), + ) + + +class OriginatorIdentifierOrKey(Choice): + schema = ( + ("issuerAndSerialNumber", IssuerAndSerialNumber()), + ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))), + ("originatorKey", OriginatorPublicKey(impl=tag_ctxc(1))), + ) + + +class UserKeyingMaterial(OctetString): + pass + + +class KeyAgreeRecipientIdentifier(Choice): + schema = ( + ("issuerAndSerialNumber", IssuerAndSerialNumber()), + # ("rKeyId", RecipientKeyIdentifier(impl=tag_ctxc(0))), + ) + + +class RecipientEncryptedKey(Sequence): + schema = ( + ("rid", KeyAgreeRecipientIdentifier()), + ("encryptedKey", EncryptedKey()), + ) + + +class RecipientEncryptedKeys(SequenceOf): + schema = RecipientEncryptedKey() + + +class KeyAgreeRecipientInfo(Sequence): + schema = ( + ("version", CMSVersion(3)), + ("originator", OriginatorIdentifierOrKey(expl=tag_ctxc(0))), + ("ukm", UserKeyingMaterial(expl=tag_ctxc(1), optional=True)), + ("keyEncryptionAlgorithm", KeyEncryptionAlgorithmIdentifier()), + ("recipientEncryptedKeys", RecipientEncryptedKeys()), + ) + + +class RecipientInfo(Choice): + schema = ( + ("ktri", KeyTransRecipientInfo()), + ("kari", KeyAgreeRecipientInfo(impl=tag_ctxc(1))), + # ("kekri", KEKRecipientInfo(impl=tag_ctxc(2))), + # ("pwri", PasswordRecipientInfo(impl=tag_ctxc(3))), + # ("ori", OtherRecipientInfo(impl=tag_ctxc(4))), + ) + + +class RecipientInfos(SetOf): + schema = RecipientInfo() + bounds = (1, float("+inf")) + + +class Gost2814789IV(OctetString): + bounds = (8, 8) + + +class Gost2814789Parameters(Sequence): + schema = ( + ("iv", Gost2814789IV()), + ("encryptionParamSet", ObjectIdentifier()), + ) + + +class Gost341215EncryptionParameters(Sequence): + schema = ( + ("ukm", OctetString()), + ) + + +class ContentEncryptionAlgorithmIdentifier(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), { + id_Gost28147_89: Gost2814789Parameters(), + id_gostr3412_2015_magma_ctracpkm: Gost341215EncryptionParameters(), + id_gostr3412_2015_kuznyechik_ctracpkm: Gost341215EncryptionParameters(), + id_gostr3412_2015_magma_ctracpkm_omac: Gost341215EncryptionParameters(), + id_gostr3412_2015_kuznyechik_ctracpkm_omac: Gost341215EncryptionParameters(), + }), + ))), + ("parameters", Any(optional=True)), + ) + + +class EncryptedContent(OctetString): + pass + + +class EncryptedContentInfo(Sequence): + schema = ( + ("contentType", ContentType()), + ("contentEncryptionAlgorithm", ContentEncryptionAlgorithmIdentifier()), + ("encryptedContent", EncryptedContent(impl=tag_ctxp(0), optional=True)), + ) + + +class Digest(OctetString): + pass + + +class AttributeValue(Any): + pass + + +class AttributeValues(SetOf): + schema = AttributeValue() + + +class EncryptedMac(OctetString): + pass + + +class Attribute(Sequence): + schema = ( + ("attrType", ObjectIdentifier(defines=( + (("attrValues",), { + id_contentType: ObjectIdentifier(), + id_messageDigest: Digest(), + id_cms_mac_attr: EncryptedMac(), + },), + ))), + ("attrValues", AttributeValues()), + ) + + +class UnprotectedAttributes(SetOf): + schema = Attribute() + bounds = (1, float("+inf")) + + +class CertificateChoices(Choice): + schema = ( + ("certificate", Certificate()), + # ("extendedCertificate", OctetString(impl=tag_ctxp(0))), + # ("v1AttrCert", AttributeCertificateV1(impl=tag_ctxc(1))), # V1 is osbolete + # ("v2AttrCert", AttributeCertificateV2(impl=tag_ctxc(2))), + # ("other", OtherCertificateFormat(impl=tag_ctxc(3))), + ) + + +class CertificateSet(SetOf): + schema = CertificateChoices() + + +class OriginatorInfo(Sequence): + schema = ( + ("certs", CertificateSet(impl=tag_ctxc(0), optional=True)), + # ("crls", RevocationInfoChoices(impl=tag_ctxc(1), optional=True)), + ) + + +class EnvelopedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("originatorInfo", OriginatorInfo(impl=tag_ctxc(0), optional=True)), + ("recipientInfos", RecipientInfos()), + ("encryptedContentInfo", EncryptedContentInfo()), + ("unprotectedAttrs", UnprotectedAttributes(impl=tag_ctxc(1), optional=True)), + ) + + +class EncapsulatedContentInfo(Sequence): + schema = ( + ("eContentType", ContentType()), + ("eContent", OctetString(expl=tag_ctxc(0), optional=True)), + ) + + +class SignerIdentifier(Choice): + schema = ( + ("issuerAndSerialNumber", IssuerAndSerialNumber()), + ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))), + ) + + +class DigestAlgorithmIdentifiers(SetOf): + schema = AlgorithmIdentifier() + + +class DigestAlgorithmIdentifier(AlgorithmIdentifier): + pass + + +class SignatureAlgorithmIdentifier(AlgorithmIdentifier): + pass + + +class SignatureValue(OctetString): + pass + + +class SignedAttributes(SetOf): + schema = Attribute() + bounds = (1, float("+inf")) + + +class SignerInfo(Sequence): + schema = ( + ("version", CMSVersion()), + ("sid", SignerIdentifier()), + ("digestAlgorithm", DigestAlgorithmIdentifier()), + ("signedAttrs", SignedAttributes(impl=tag_ctxc(0), optional=True)), + ("signatureAlgorithm", SignatureAlgorithmIdentifier()), + ("signature", SignatureValue()), + # ("unsignedAttrs", UnsignedAttributes(impl=tag_ctxc(1), optional=True)), + ) + + +class SignerInfos(SetOf): + schema = SignerInfo() + + +class SignedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("digestAlgorithms", DigestAlgorithmIdentifiers()), + ("encapContentInfo", EncapsulatedContentInfo()), + ("certificates", CertificateSet(impl=tag_ctxc(0), optional=True)), + # ("crls", RevocationInfoChoices(impl=tag_ctxc(1), optional=True)), + ("signerInfos", SignerInfos()), + ) + + +class DigestedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("digestAlgorithm", DigestAlgorithmIdentifier()), + ("encapContentInfo", EncapsulatedContentInfo()), + ("digest", Digest()), + ) + + +class EncryptedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("encryptedContentInfo", EncryptedContentInfo()), + ("unprotectedAttrs", UnprotectedAttributes(impl=tag_ctxc(1), optional=True)), + ) + + +class ContentInfo(Sequence): + schema = ( + ("contentType", ContentType(defines=( + (("content",), { + id_digestedData: DigestedData(), + id_encryptedData: EncryptedData(), + id_envelopedData: EnvelopedData(), + id_signedData: SignedData(), + }), + ))), + ("content", Any(expl=tag_ctxc(0))), + ) diff --git a/pygost-5.13/build/lib/pygost/asn1schemas/oids.py b/pygost-5.13/build/lib/pygost/asn1schemas/oids.py new file mode 100644 index 0000000..4638900 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/asn1schemas/oids.py @@ -0,0 +1,60 @@ +from pyderasn import ObjectIdentifier + + +id_at_commonName = ObjectIdentifier("2.5.4.3") +id_at_countryName = ObjectIdentifier("2.5.4.6") +id_at_localityName = ObjectIdentifier("2.5.4.7") +id_at_stateOrProvinceName = ObjectIdentifier("2.5.4.8") +id_at_organizationName = ObjectIdentifier("2.5.4.10") + +id_pkcs7 = ObjectIdentifier("1.2.840.113549.1.7") +id_data = id_pkcs7 + (1,) +id_signedData = id_pkcs7 + (2,) +id_envelopedData = id_pkcs7 + (3,) +id_digestedData = id_pkcs7 + (5,) +id_encryptedData = id_pkcs7 + (6,) + +id_pkcs9 = ObjectIdentifier("1.2.840.113549.1.9") +id_contentType = id_pkcs9 + (3,) +id_messageDigest = id_pkcs9 + (4,) +id_pkcs9_certTypes_x509Certificate = ObjectIdentifier("1.2.840.113549.1.9.22.1") +id_pkcs12_bagtypes_keyBag = ObjectIdentifier("1.2.840.113549.1.12.10.1.1") +id_pkcs12_bagtypes_pkcs8ShroudedKeyBag = ObjectIdentifier("1.2.840.113549.1.12.10.1.2") +id_pkcs12_bagtypes_certBag = ObjectIdentifier("1.2.840.113549.1.12.10.1.3") + +id_Gost28147_89 = ObjectIdentifier("1.2.643.2.2.21") +id_GostR3410_2001_TestParamSet = ObjectIdentifier("1.2.643.2.2.35.0") +id_cms_mac_attr = ObjectIdentifier("1.2.643.7.1.0.6.1.1") +id_tc26_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.1.1") +id_tc26_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.1.2") +id_tc26_gost3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.2.2") +id_tc26_gost3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.2.3") +id_tc26_signwithdigest_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2") +id_tc26_signwithdigest_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3") +id_gostr3412_2015_magma_ctracpkm = ObjectIdentifier("1.2.643.7.1.1.5.1.1") +id_gostr3412_2015_magma_ctracpkm_omac = ObjectIdentifier("1.2.643.7.1.1.5.1.2") +id_gostr3412_2015_kuznyechik_ctracpkm = ObjectIdentifier("1.2.643.7.1.1.5.2.1") +id_gostr3412_2015_kuznyechik_ctracpkm_omac = ObjectIdentifier("1.2.643.7.1.1.5.2.2") +id_tc26_agreement_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.6.1") +id_tc26_agreement_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.6.2") +id_gostr3412_2015_magma_wrap_kexp15 = ObjectIdentifier("1.2.643.7.1.1.7.1.1") +id_gostr3412_2015_kuznyechik_wrap_kexp15 = ObjectIdentifier("1.2.643.7.1.1.7.2.1") +id_tc26_gost3410_2012_256_paramSetA = ObjectIdentifier("1.2.643.7.1.2.1.1.1") +id_tc26_gost3410_2012_256_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.1.2") +id_tc26_gost3410_2012_256_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.1.3") +id_tc26_gost3410_2012_256_paramSetD = ObjectIdentifier("1.2.643.7.1.2.1.1.4") +id_tc26_gost3410_2012_512_paramSetTest = ObjectIdentifier("1.2.643.7.1.2.1.2.0") +id_tc26_gost3410_2012_512_paramSetA = ObjectIdentifier("1.2.643.7.1.2.1.2.1") +id_tc26_gost3410_2012_512_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.2.2") +id_tc26_gost3410_2012_512_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.2.3") +id_tc26_gost_28147_param_Z = ObjectIdentifier("1.2.643.7.1.2.5.1.1") + +id_pbes2 = ObjectIdentifier("1.2.840.113549.1.5.13") +id_pbkdf2 = ObjectIdentifier("1.2.840.113549.1.5.12") + +id_at_commonName = ObjectIdentifier("2.5.4.3") +id_ce_basicConstraints = ObjectIdentifier("2.5.29.19") +id_ce_subjectKeyIdentifier = ObjectIdentifier("2.5.29.14") +id_ce_keyUsage = ObjectIdentifier("2.5.29.15") +id_ce_subjectAltName = ObjectIdentifier("2.5.29.17") +id_ce_authorityKeyIdentifier = ObjectIdentifier("2.5.29.35") diff --git a/pygost-5.13/build/lib/pygost/asn1schemas/pfx.py b/pygost-5.13/build/lib/pygost/asn1schemas/pfx.py new file mode 100644 index 0000000..27a87d0 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/asn1schemas/pfx.py @@ -0,0 +1,250 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""PKCS #12 related structures (**NOT COMPLETE**) +""" + +from pyderasn import Any +from pyderasn import Choice +from pyderasn import Integer +from pyderasn import ObjectIdentifier +from pyderasn import OctetString +from pyderasn import Sequence +from pyderasn import SequenceOf +from pyderasn import SetOf +from pyderasn import tag_ctxc +from pyderasn import tag_ctxp + +from pygost.asn1schemas.cms import CMSVersion +from pygost.asn1schemas.cms import ContentType +from pygost.asn1schemas.cms import Gost2814789Parameters +from pygost.asn1schemas.cms import Gost341215EncryptionParameters +from pygost.asn1schemas.oids import id_data +from pygost.asn1schemas.oids import id_encryptedData +from pygost.asn1schemas.oids import id_Gost28147_89 +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm_omac +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm_omac +from pygost.asn1schemas.oids import id_pbes2 +from pygost.asn1schemas.oids import id_pbkdf2 +from pygost.asn1schemas.oids import id_pkcs9_certTypes_x509Certificate +from pygost.asn1schemas.prvkey import PrivateKeyInfo +from pygost.asn1schemas.x509 import AlgorithmIdentifier +from pygost.asn1schemas.x509 import Certificate + + +class PBKDF2Salt(Choice): + schema = ( + ("specified", OctetString()), + # ("otherSource", PBKDF2SaltSources()), + ) + + +id_hmacWithSHA1 = ObjectIdentifier("1.2.840.113549.2.7") + + +class PBKDF2PRFs(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(default=id_hmacWithSHA1)), + ("parameters", Any(optional=True)), + ) + + +class IterationCount(Integer): + bounds = (1, float("+inf")) + + +class KeyLength(Integer): + bounds = (1, float("+inf")) + + +class PBKDF2Params(Sequence): + schema = ( + ("salt", PBKDF2Salt()), + ("iterationCount", IterationCount(optional=True)), + ("keyLength", KeyLength(optional=True)), + ("prf", PBKDF2PRFs()), + ) + + +class PBES2KDFs(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), {id_pbkdf2: PBKDF2Params()}), + ))), + ("parameters", Any(optional=True)), + ) + + +class PBES2Encs(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), { + id_Gost28147_89: Gost2814789Parameters(), + id_gostr3412_2015_magma_ctracpkm: Gost341215EncryptionParameters(), + id_gostr3412_2015_magma_ctracpkm_omac: Gost341215EncryptionParameters(), + id_gostr3412_2015_kuznyechik_ctracpkm: Gost341215EncryptionParameters(), + id_gostr3412_2015_kuznyechik_ctracpkm_omac: Gost341215EncryptionParameters(), + }), + ))), + ("parameters", Any(optional=True)), + ) + + +class PBES2Params(Sequence): + schema = ( + ("keyDerivationFunc", PBES2KDFs()), + ("encryptionScheme", PBES2Encs()), + ) + + +class EncryptionAlgorithmIdentifier(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), {id_pbes2: PBES2Params()}), + ))), + ("parameters", Any(optional=True)), + ) + + +class ContentEncryptionAlgorithmIdentifier(EncryptionAlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), {id_pbes2: PBES2Params()}), + ))), + ("parameters", Any(optional=True)), + ) + + +class EncryptedContent(OctetString): + pass + + +class EncryptedContentInfo(Sequence): + schema = ( + ("contentType", ContentType()), + ("contentEncryptionAlgorithm", ContentEncryptionAlgorithmIdentifier()), + ("encryptedContent", EncryptedContent(impl=tag_ctxp(0), optional=True)), + ) + + +class EncryptedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("encryptedContentInfo", EncryptedContentInfo()), + # ("unprotectedAttrs", UnprotectedAttributes(impl=tag_ctxc(1), optional=True)), + ) + + +class PKCS12BagSet(Any): + pass + + +class AttrValue(SetOf): + schema = Any() + + +class PKCS12Attribute(Sequence): + schema = ( + ("attrId", ObjectIdentifier()), + ("attrValue", AttrValue()), + ) + + +class PKCS12Attributes(SetOf): + schema = PKCS12Attribute() + + +class SafeBag(Sequence): + schema = ( + ("bagId", ObjectIdentifier(defines=( + (("bagValue",), {id_encryptedData: EncryptedData()}), + ))), + ("bagValue", PKCS12BagSet(expl=tag_ctxc(0))), + ("bagAttributes", PKCS12Attributes(optional=True)), + ) + + +class SafeContents(SequenceOf): + schema = SafeBag() + + +OctetStringSafeContents = SafeContents(expl=OctetString.tag_default) + + +class AuthSafe(Sequence): + schema = ( + ("contentType", ContentType(defines=( + (("content",), {id_data: OctetStringSafeContents()}), + ))), + ("content", Any(expl=tag_ctxc(0))), + ) + + +class DigestInfo(Sequence): + schema = ( + ("digestAlgorithm", AlgorithmIdentifier()), + ("digest", OctetString()), + ) + + +class MacData(Sequence): + schema = ( + ("mac", DigestInfo()), + ("macSalt", OctetString()), + ("iterations", Integer(default=1)), + ) + + +class PFX(Sequence): + schema = ( + ("version", Integer(default=1)), + ("authSafe", AuthSafe()), + ("macData", MacData(optional=True)), + ) + + +class EncryptedPrivateKeyInfo(Sequence): + schema = ( + ("encryptionAlgorithm", EncryptionAlgorithmIdentifier()), + ("encryptedData", OctetString()), + ) + + +class PKCS8ShroudedKeyBag(EncryptedPrivateKeyInfo): + pass + + +OctetStringX509Certificate = Certificate(expl=OctetString.tag_default) + + +class CertTypes(Any): + pass + + +class CertBag(Sequence): + schema = ( + ("certId", ObjectIdentifier(defines=( + (("certValue",), { + id_pkcs9_certTypes_x509Certificate: OctetStringX509Certificate(), + }), + ))), + ("certValue", CertTypes(expl=tag_ctxc(0))), + ) + + +class KeyBag(PrivateKeyInfo): + pass diff --git a/pygost-5.13/build/lib/pygost/asn1schemas/pkcs10.py b/pygost-5.13/build/lib/pygost/asn1schemas/pkcs10.py new file mode 100644 index 0000000..dce45dd --- /dev/null +++ b/pygost-5.13/build/lib/pygost/asn1schemas/pkcs10.py @@ -0,0 +1,49 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""PKCS #10 related structures (**NOT COMPLETE**) +""" + +from pyderasn import BitString +from pyderasn import Integer +from pyderasn import Sequence +from pyderasn import SetOf +from pyderasn import tag_ctxc + +from pygost.asn1schemas.cms import Attribute +from pygost.asn1schemas.x509 import AlgorithmIdentifier +from pygost.asn1schemas.x509 import Name +from pygost.asn1schemas.x509 import SubjectPublicKeyInfo + + +class Attributes(SetOf): + schema = Attribute() + + +class CertificationRequestInfo(Sequence): + schema = ( + ("version", Integer(0)), + ("subject", Name()), + ("subjectPKInfo", SubjectPublicKeyInfo()), + ("attributes", Attributes(impl=tag_ctxc(0))), + ) + + +class CertificationRequest(Sequence): + schema = ( + ("certificationRequestInfo", CertificationRequestInfo()), + ("signatureAlgorithm", AlgorithmIdentifier()), + ("signature", BitString()), + ) diff --git a/pygost-5.13/build/lib/pygost/asn1schemas/prvkey.py b/pygost-5.13/build/lib/pygost/asn1schemas/prvkey.py new file mode 100644 index 0000000..7da2533 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/asn1schemas/prvkey.py @@ -0,0 +1,100 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from pyderasn import Any +from pyderasn import BitString +from pyderasn import Choice +from pyderasn import Integer +from pyderasn import Null +from pyderasn import ObjectIdentifier +from pyderasn import OctetString +from pyderasn import Sequence +from pyderasn import SetOf +from pyderasn import tag_ctxc +from pyderasn import tag_ctxp + +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 +from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters + + +class ECParameters(Choice): + schema = ( + ("namedCurve", ObjectIdentifier()), + ("implicitCurve", Null()), + # ("specifiedCurve", SpecifiedECDomain()), + ) + + +ecPrivkeyVer1 = Integer(1) + + +class ECPrivateKey(Sequence): + schema = ( + ("version", Integer(ecPrivkeyVer1)), + ("privateKey", OctetString()), + ("parameters", ECParameters(expl=tag_ctxc(0), optional=True)), + ("publicKey", BitString(expl=tag_ctxc(1), optional=True)), + ) + + +class PrivateKeyAlgorithmIdentifier(Sequence): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), { + id_tc26_gost3410_2012_256: GostR34102012PublicKeyParameters(), + id_tc26_gost3410_2012_512: GostR34102012PublicKeyParameters(), + }), + ))), + ("parameters", Any(optional=True)), + ) + + +class PrivateKey(OctetString): + pass + + +class AttributeValue(Any): + pass + + +class AttributeValues(SetOf): + schema = AttributeValue() + + +class Attribute(Sequence): + schema = ( + ("attrType", ObjectIdentifier()), + ("attrValues", AttributeValues()), + ) + + +class Attributes(SetOf): + schema = Attribute() + + +class PublicKey(BitString): + pass + + +class PrivateKeyInfo(Sequence): + schema = ( + ("version", Integer(0)), + ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier()), + ("privateKey", PrivateKey()), + ("attributes", Attributes(impl=tag_ctxc(0), optional=True)), + ("publicKey", PublicKey(impl=tag_ctxp(1), optional=True)), + ) diff --git a/pygost-5.13/build/lib/pygost/asn1schemas/x509.py b/pygost-5.13/build/lib/pygost/asn1schemas/x509.py new file mode 100644 index 0000000..86ad7da --- /dev/null +++ b/pygost-5.13/build/lib/pygost/asn1schemas/x509.py @@ -0,0 +1,262 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""":rfc:`5280` related structures (**NOT COMPLETE**) + +They are taken from `PyDERASN +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST 28147-89 block cipher + +This is implementation of :rfc:`5830` ECB, CNT, CFB and :rfc:`4357` +CBC modes of operation. N1, N2, K names are taken according to +specification's terminology. CNT and CFB modes can work with arbitrary +data lengths. +""" + +from functools import partial + +from pygost.gost3413 import pad2 +from pygost.gost3413 import pad_size +from pygost.gost3413 import unpad2 +from pygost.utils import hexdec +from pygost.utils import strxor +from pygost.utils import xrange + + +KEYSIZE = 32 +BLOCKSIZE = 8 +C1 = 0x01010104 +C2 = 0x01010101 + +# Sequence of K_i S-box applying for encryption and decryption +SEQ_ENCRYPT = ( + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 7, 6, 5, 4, 3, 2, 1, 0, +) +SEQ_DECRYPT = ( + 0, 1, 2, 3, 4, 5, 6, 7, + 7, 6, 5, 4, 3, 2, 1, 0, + 7, 6, 5, 4, 3, 2, 1, 0, + 7, 6, 5, 4, 3, 2, 1, 0, +) + +# S-box parameters +DEFAULT_SBOX = "id-Gost28147-89-CryptoPro-A-ParamSet" +SBOXES = { + "id-Gost28147-89-TestParamSet": ( + (4, 2, 15, 5, 9, 1, 0, 8, 14, 3, 11, 12, 13, 7, 10, 6), + (12, 9, 15, 14, 8, 1, 3, 10, 2, 7, 4, 13, 6, 0, 11, 5), + (13, 8, 14, 12, 7, 3, 9, 10, 1, 5, 2, 4, 6, 15, 0, 11), + (14, 9, 11, 2, 5, 15, 7, 1, 0, 13, 12, 6, 10, 4, 3, 8), + (3, 14, 5, 9, 6, 8, 0, 13, 10, 11, 7, 12, 2, 1, 15, 4), + (8, 15, 6, 11, 1, 9, 12, 5, 13, 3, 7, 10, 0, 14, 2, 4), + (9, 11, 12, 0, 3, 6, 7, 5, 4, 8, 14, 15, 1, 10, 2, 13), + (12, 6, 5, 2, 11, 0, 9, 13, 3, 14, 7, 10, 15, 4, 1, 8), + ), + "id-Gost28147-89-CryptoPro-A-ParamSet": ( + (9, 6, 3, 2, 8, 11, 1, 7, 10, 4, 14, 15, 12, 0, 13, 5), + (3, 7, 14, 9, 8, 10, 15, 0, 5, 2, 6, 12, 11, 4, 13, 1), + (14, 4, 6, 2, 11, 3, 13, 8, 12, 15, 5, 10, 0, 7, 1, 9), + (14, 7, 10, 12, 13, 1, 3, 9, 0, 2, 11, 4, 15, 8, 5, 6), + (11, 5, 1, 9, 8, 13, 15, 0, 14, 4, 2, 3, 12, 7, 10, 6), + (3, 10, 13, 12, 1, 2, 0, 11, 7, 5, 9, 4, 8, 15, 14, 6), + (1, 13, 2, 9, 7, 10, 6, 0, 8, 12, 4, 5, 15, 3, 11, 14), + (11, 10, 15, 5, 0, 12, 14, 8, 6, 2, 3, 9, 1, 7, 13, 4), + ), + "id-Gost28147-89-CryptoPro-B-ParamSet": ( + (8, 4, 11, 1, 3, 5, 0, 9, 2, 14, 10, 12, 13, 6, 7, 15), + (0, 1, 2, 10, 4, 13, 5, 12, 9, 7, 3, 15, 11, 8, 6, 14), + (14, 12, 0, 10, 9, 2, 13, 11, 7, 5, 8, 15, 3, 6, 1, 4), + (7, 5, 0, 13, 11, 6, 1, 2, 3, 10, 12, 15, 4, 14, 9, 8), + (2, 7, 12, 15, 9, 5, 10, 11, 1, 4, 0, 13, 6, 8, 14, 3), + (8, 3, 2, 6, 4, 13, 14, 11, 12, 1, 7, 15, 10, 0, 9, 5), + (5, 2, 10, 11, 9, 1, 12, 3, 7, 4, 13, 0, 6, 15, 8, 14), + (0, 4, 11, 14, 8, 3, 7, 1, 10, 2, 9, 6, 15, 13, 5, 12), + ), + "id-Gost28147-89-CryptoPro-C-ParamSet": ( + (1, 11, 12, 2, 9, 13, 0, 15, 4, 5, 8, 14, 10, 7, 6, 3), + (0, 1, 7, 13, 11, 4, 5, 2, 8, 14, 15, 12, 9, 10, 6, 3), + (8, 2, 5, 0, 4, 9, 15, 10, 3, 7, 12, 13, 6, 14, 1, 11), + (3, 6, 0, 1, 5, 13, 10, 8, 11, 2, 9, 7, 14, 15, 12, 4), + (8, 13, 11, 0, 4, 5, 1, 2, 9, 3, 12, 14, 6, 15, 10, 7), + (12, 9, 11, 1, 8, 14, 2, 4, 7, 3, 6, 5, 10, 0, 15, 13), + (10, 9, 6, 8, 13, 14, 2, 0, 15, 3, 5, 11, 4, 1, 12, 7), + (7, 4, 0, 5, 10, 2, 15, 14, 12, 6, 1, 11, 13, 9, 3, 8), + ), + "id-Gost28147-89-CryptoPro-D-ParamSet": ( + (15, 12, 2, 10, 6, 4, 5, 0, 7, 9, 14, 13, 1, 11, 8, 3), + (11, 6, 3, 4, 12, 15, 14, 2, 7, 13, 8, 0, 5, 10, 9, 1), + (1, 12, 11, 0, 15, 14, 6, 5, 10, 13, 4, 8, 9, 3, 7, 2), + (1, 5, 14, 12, 10, 7, 0, 13, 6, 2, 11, 4, 9, 3, 15, 8), + (0, 12, 8, 9, 13, 2, 10, 11, 7, 3, 6, 5, 4, 14, 15, 1), + (8, 0, 15, 3, 2, 5, 14, 11, 1, 10, 4, 7, 12, 9, 13, 6), + (3, 0, 6, 15, 1, 14, 9, 2, 13, 8, 12, 4, 11, 10, 5, 7), + (1, 10, 6, 8, 15, 11, 0, 4, 12, 3, 5, 9, 7, 13, 2, 14), + ), + "id-tc26-gost-28147-param-Z": ( + (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1), + (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15), + (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0), + (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11), + (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12), + (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0), + (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7), + (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2), + ), + "id-GostR3411-94-TestParamSet": ( + (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3), + (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9), + (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11), + (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3), + (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2), + (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14), + (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12), + (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12), + ), + "id-GostR3411-94-CryptoProParamSet": ( + (10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15), + (5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8), + (7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13), + (4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3), + (7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5), + (7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3), + (13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11), + (1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12), + ), + "EACParamSet": ( + (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13), + (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2), + (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14), + (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13), + (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2), + (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10), + (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15), + (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1), + ), +} +SBOXES["AppliedCryptography"] = SBOXES["id-GostR3411-94-TestParamSet"] + + +def _K(s, _in): + """S-box substitution + + :param s: S-box + :param _in: 32-bit word + :returns: substituted 32-bit word + """ + return ( + (s[0][(_in >> 0) & 0x0F] << 0) + + (s[1][(_in >> 4) & 0x0F] << 4) + + (s[2][(_in >> 8) & 0x0F] << 8) + + (s[3][(_in >> 12) & 0x0F] << 12) + + (s[4][(_in >> 16) & 0x0F] << 16) + + (s[5][(_in >> 20) & 0x0F] << 20) + + (s[6][(_in >> 24) & 0x0F] << 24) + + (s[7][(_in >> 28) & 0x0F] << 28) + ) + + +def block2ns(data): + """Convert block to N1 and N2 integers + """ + data = bytearray(data) + return ( + data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24, + data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24, + ) + + +def ns2block(ns): + """Convert N1 and N2 integers to 8-byte block + """ + n1, n2 = ns + return bytes(bytearray(( + (n2 >> 0) & 0xFF, (n2 >> 8) & 0xFF, (n2 >> 16) & 0xFF, (n2 >> 24) & 0xFF, + (n1 >> 0) & 0xFF, (n1 >> 8) & 0xFF, (n1 >> 16) & 0xFF, (n1 >> 24) & 0xFF, + ))) + + +def _shift11(x): + """11-bit cyclic shift + """ + return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1)) + + +def validate_key(key): + if len(key) != KEYSIZE: + raise ValueError("Invalid key size") + + +def validate_iv(iv): + if len(iv) != BLOCKSIZE: + raise ValueError("Invalid IV size") + + +def validate_sbox(sbox): + if sbox not in SBOXES: + raise ValueError("Unknown sbox supplied") + + +def xcrypt(seq, sbox, key, ns): + """Perform full-round single-block operation + + :param seq: sequence of K_i S-box applying (either encrypt or decrypt) + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bytes key: 256-bit encryption key + :param ns: N1 and N2 integers + :type ns: (int, int) + :returns: resulting N1 and N2 + :rtype: (int, int) + """ + s = SBOXES[sbox] + w = bytearray(key) + x = [ + w[0 + i * 4] | + w[1 + i * 4] << 8 | + w[2 + i * 4] << 16 | + w[3 + i * 4] << 24 for i in range(8) + ] + n1, n2 = ns + for i in seq: + n1, n2 = _shift11(_K(s, (n1 + x[i]) % (2 ** 32))) ^ n2, n1 + return n1, n2 + + +def encrypt(sbox, key, ns): + """Encrypt single block + """ + return xcrypt(SEQ_ENCRYPT, sbox, key, ns) + + +def decrypt(sbox, key, ns): + """Decrypt single block + """ + return xcrypt(SEQ_DECRYPT, sbox, key, ns) + + +def ecb(key, data, action, sbox=DEFAULT_SBOX): + """ECB mode of operation + + :param bytes key: encryption key + :param data: plaintext + :type data: bytes, multiple of BLOCKSIZE + :param func action: "encrypt"/"decrypt" + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :returns: ciphertext + :rtype: bytes + """ + validate_key(key) + validate_sbox(sbox) + if not data or len(data) % BLOCKSIZE != 0: + raise ValueError("Data is not blocksize aligned") + result = [] + for i in xrange(0, len(data), BLOCKSIZE): + result.append(ns2block(action( + sbox, key, block2ns(data[i:i + BLOCKSIZE]) + ))) + return b"".join(result) + + +ecb_encrypt = partial(ecb, action=encrypt) +ecb_decrypt = partial(ecb, action=decrypt) + + +def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX, mesh=False): + """CBC encryption mode of operation + + :param bytes key: encryption key + :param bytes data: plaintext + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :type bool pad: perform ISO/IEC 7816-4 padding + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing + :returns: ciphertext + :rtype: bytes + + 34.13-2015 padding method 2 is used. + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + if not data: + raise ValueError("No data supplied") + if pad: + data = pad2(data, BLOCKSIZE) + if len(data) % BLOCKSIZE != 0: + raise ValueError("Data is not blocksize aligned") + ciphertext = [iv] + for i in xrange(0, len(data), BLOCKSIZE): + if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0: + key, _ = meshing(key, iv, sbox=sbox) + ciphertext.append(ns2block(encrypt(sbox, key, block2ns( + strxor(ciphertext[-1], data[i:i + BLOCKSIZE]) + )))) + return b"".join(ciphertext) + + +def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False): + """CBC decryption mode of operation + + :param bytes key: encryption key + :param bytes data: ciphertext + :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing + :returns: plaintext + :rtype: bytes + """ + validate_key(key) + validate_sbox(sbox) + if not data or len(data) % BLOCKSIZE != 0: + raise ValueError("Data is not blocksize aligned") + if len(data) < 2 * BLOCKSIZE: + raise ValueError("There is no either data, or IV in ciphertext") + iv = data[:BLOCKSIZE] + plaintext = [] + for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE): + if ( + mesh and + (i - BLOCKSIZE) >= MESH_MAX_DATA and + (i - BLOCKSIZE) % MESH_MAX_DATA == 0 + ): + key, _ = meshing(key, iv, sbox=sbox) + plaintext.append(strxor( + ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))), + data[i - BLOCKSIZE:i], + )) + if pad: + plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE) + return b"".join(plaintext) + + +def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX): + """Counter mode of operation + + :param bytes key: encryption key + :param bytes data: plaintext + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :returns: ciphertext + :rtype: bytes + + For decryption you use the same function again. + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + if not data: + raise ValueError("No data supplied") + n2, n1 = encrypt(sbox, key, block2ns(iv)) + gamma = [] + for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE): + n1 = (n1 + C2) % (2 ** 32) + n2 = (n2 + C1) % (2 ** 32 - 1) + gamma.append(ns2block(encrypt(sbox, key, (n1, n2)))) + return strxor(b"".join(gamma), data) + + +MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B") +MESH_MAX_DATA = 1024 + + +def meshing(key, iv, sbox=DEFAULT_SBOX): + """:rfc:`4357` key meshing + """ + key = ecb_decrypt(key, MESH_CONST, sbox=sbox) + iv = ecb_encrypt(key, iv, sbox=sbox) + return key, iv + + +def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False): + """CFB encryption mode of operation + + :param bytes key: encryption key + :param bytes data: plaintext + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing + :returns: ciphertext + :rtype: bytes + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + if not data: + raise ValueError("No data supplied") + ciphertext = [iv] + for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE): + if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0: + key, iv = meshing(key, ciphertext[-1], sbox=sbox) + ciphertext.append(strxor( + data[i:i + BLOCKSIZE], + ns2block(encrypt(sbox, key, block2ns(iv))), + )) + continue + ciphertext.append(strxor( + data[i:i + BLOCKSIZE], + ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))), + )) + return b"".join(ciphertext[1:]) + + +def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False): + """CFB decryption mode of operation + + :param bytes key: encryption key + :param bytes data: plaintext + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing + :returns: ciphertext + :rtype: bytes + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + if not data: + raise ValueError("No data supplied") + plaintext = [] + data = iv + data + for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE): + if ( + mesh and + (i - BLOCKSIZE) >= MESH_MAX_DATA and + (i - BLOCKSIZE) % MESH_MAX_DATA == 0 + ): + key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox) + plaintext.append(strxor( + data[i:i + BLOCKSIZE], + ns2block(encrypt(sbox, key, block2ns(iv))), + )) + continue + plaintext.append(strxor( + data[i:i + BLOCKSIZE], + ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))), + )) + return b"".join(plaintext) diff --git a/pygost-5.13/build/lib/pygost/gost28147_mac.py b/pygost-5.13/build/lib/pygost/gost28147_mac.py new file mode 100644 index 0000000..aab2805 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/gost28147_mac.py @@ -0,0 +1,99 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST 28147-89 MAC +""" + +from copy import copy + +from pygost.gost28147 import block2ns +from pygost.gost28147 import BLOCKSIZE +from pygost.gost28147 import DEFAULT_SBOX +from pygost.gost28147 import ns2block +from pygost.gost28147 import validate_iv +from pygost.gost28147 import validate_key +from pygost.gost28147 import validate_sbox +from pygost.gost28147 import xcrypt +from pygost.gost3413 import pad1 +from pygost.iface import PEP247 +from pygost.utils import strxor +from pygost.utils import xrange + +digest_size = 8 +SEQ_MAC = ( + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, +) + + +class MAC(PEP247): + """GOST 28147-89 MAC mode of operation + + >>> m = MAC(key=key) + >>> m.update("some data") + >>> m.update("another data") + >>> m.hexdigest()[:8] + 'a687a08b' + """ + digest_size = digest_size + + def __init__(self, key, data=b"", iv=8 * b"\x00", sbox=DEFAULT_SBOX): + """ + :param key: authentication key + :type key: bytes, 32 bytes + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + self.key = key + self.data = data + self.iv = iv + self.sbox = sbox + + def copy(self): + return MAC(self.key, copy(self.data), self.iv, self.sbox) + + def update(self, data): + """Append data that has to be authenticated + """ + self.data += data + + def digest(self): + """Get MAC tag of supplied data + + You have to provide at least single byte of data. + If you want to produce tag length of 3 bytes, then + ``digest()[:3]``. + """ + if not self.data: + raise ValueError("No data processed") + data = pad1(self.data, BLOCKSIZE) + prev = block2ns(self.iv)[::-1] + for i in xrange(0, len(data), BLOCKSIZE): + prev = xcrypt( + SEQ_MAC, self.sbox, self.key, block2ns(strxor( + data[i:i + BLOCKSIZE], + ns2block(prev), + )), + )[::-1] + return ns2block(prev) + + +def new(key, data=b"", iv=8 * b"\x00", sbox=DEFAULT_SBOX): + return MAC(key, data, iv, sbox) diff --git a/pygost-5.13/build/lib/pygost/gost3410.py b/pygost-5.13/build/lib/pygost/gost3410.py new file mode 100644 index 0000000..c12b170 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/gost3410.py @@ -0,0 +1,412 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST R 34.10 public-key signature function. + +This is implementation of GOST R 34.10-2001 (:rfc:`5832`), GOST R +34.10-2012 (:rfc:`7091`). The difference between 2001 and 2012 is the +key, digest and signature lengths. +""" + +from os import urandom + +from pygost.utils import bytes2long +from pygost.utils import hexdec +from pygost.utils import long2bytes +from pygost.utils import modinvert + + +def point_size(point): + """Determine is it either 256 or 512 bit point + """ + return (512 // 8) if point.bit_length() > 256 else (256 // 8) + + +class GOST3410Curve(object): + """GOST 34.10 validated curve + + >>> curve = CURVES["id-GostR3410-2001-TestParamSet"] + >>> prv = prv_unmarshal(urandom(32)) + >>> signature = sign(curve, prv, GOST341194(data).digest()) + >>> pub = public_key(curve, prv) + >>> verify(curve, pub, GOST341194(data).digest(), signature) + True + + :param long p: characteristic of the underlying prime field + :param long q: elliptic curve subgroup order + :param long a, b: coefficients of the equation of the elliptic curve in + the canonical form + :param long x, y: the coordinate of the point P (generator of the + subgroup of order q) of the elliptic curve in + the canonical form + :param long e, d: coefficients of the equation of the elliptic curve in + the twisted Edwards form + :param str name: human-readable curve name + """ + + def __init__(self, p, q, a, b, x, y, cofactor=1, e=None, d=None, name=None): + self.p = p + self.q = q + self.a = a + self.b = b + self.x = x + self.y = y + self.cofactor = cofactor + self.e = e + self.d = d + if not self.contains((x, y)): + raise ValueError("Invalid parameters") + self._st = None + self.name = name + + @property + def point_size(self): + return point_size(self.p) + + def __repr__(self): + return "<%s: %s>" % (self.__class__.__name__, self.name) + + def pos(self, v): + """Make positive number + """ + if v < 0: + return v + self.p + return v + + def contains(self, point): + """Is point on the curve? + + :type point: (long, long) + """ + x, y = point + r1 = y * y % self.p + r2 = ((x * x + self.a) * x + self.b) % self.p + return r1 == self.pos(r2) + + def _add(self, p1x, p1y, p2x, p2y): + if p1x == p2x and p1y == p2y: + # double + t = ((3 * p1x * p1x + self.a) * modinvert(2 * p1y, self.p)) % self.p + else: + tx = self.pos(p2x - p1x) % self.p + ty = self.pos(p2y - p1y) % self.p + t = (ty * modinvert(tx, self.p)) % self.p + tx = self.pos(t * t - p1x - p2x) % self.p + ty = self.pos(t * (p1x - tx) - p1y) % self.p + return tx, ty + + def exp(self, degree, x=None, y=None): + x = x or self.x + y = y or self.y + tx = x + ty = y + if degree == 0: + raise ValueError("Bad degree value") + degree -= 1 + while degree != 0: + if degree & 1 == 1: + tx, ty = self._add(tx, ty, x, y) + degree = degree >> 1 + x, y = self._add(x, y, x, y) + return tx, ty + + def st(self): + """Compute s/t parameters for twisted Edwards curve points conversion + """ + if self.e is None or self.d is None: + raise ValueError("Non twisted Edwards curve") + if self._st is not None: + return self._st + self._st = ( + self.pos(self.e - self.d) * modinvert(4, self.p) % self.p, + (self.e + self.d) * modinvert(6, self.p) % self.p, + ) + return self._st + + +CURVES = { + "GostR3410_2001_ParamSet_cc": GOST3410Curve( + p=bytes2long(hexdec("C0000000000000000000000000000000000000000000000000000000000003C7")), + q=bytes2long(hexdec("5fffffffffffffffffffffffffffffff606117a2f4bde428b7458a54b6e87b85")), + a=bytes2long(hexdec("C0000000000000000000000000000000000000000000000000000000000003c4")), + b=bytes2long(hexdec("2d06B4265ebc749ff7d0f1f1f88232e81632e9088fd44b7787d5e407e955080c")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000002")), + y=bytes2long(hexdec("a20e034bf8813ef5c18d01105e726a17eb248b264ae9706f440bedc8ccb6b22c")), + ), + "id-GostR3410-2001-TestParamSet": GOST3410Curve( + p=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000431")), + q=bytes2long(hexdec("8000000000000000000000000000000150FE8A1892976154C59CFC193ACCF5B3")), + a=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000007")), + b=bytes2long(hexdec("5FBFF498AA938CE739B8E022FBAFEF40563F6E6A3472FC2A514C0CE9DAE23B7E")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000002")), + y=bytes2long(hexdec("08E2A8A0E65147D4BD6316030E16D19C85C97F0A9CA267122B96ABBCEA7E8FC8")), + ), + "id-tc26-gost-3410-12-256-paramSetA": GOST3410Curve( + p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97")), + q=bytes2long(hexdec("400000000000000000000000000000000FD8CDDFC87B6635C115AF556C360C67")), + a=bytes2long(hexdec("C2173F1513981673AF4892C23035A27CE25E2013BF95AA33B22C656F277E7335")), + b=bytes2long(hexdec("295F9BAE7428ED9CCC20E7C359A9D41A22FCCD9108E17BF7BA9337A6F8AE9513")), + x=bytes2long(hexdec("91E38443A5E82C0D880923425712B2BB658B9196932E02C78B2582FE742DAA28")), + y=bytes2long(hexdec("32879423AB1A0375895786C4BB46E9565FDE0B5344766740AF268ADB32322E5C")), + cofactor=4, + e=0x01, + d=bytes2long(hexdec("0605F6B7C183FA81578BC39CFAD518132B9DF62897009AF7E522C32D6DC7BFFB")), + ), + "id-tc26-gost-3410-12-256-paramSetB": GOST3410Curve( + p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97")), + q=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893")), + a=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94")), + b=bytes2long(hexdec("00000000000000000000000000000000000000000000000000000000000000a6")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000001")), + y=bytes2long(hexdec("8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14")), + ), + "id-tc26-gost-3410-12-256-paramSetC": GOST3410Curve( + p=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000C99")), + q=bytes2long(hexdec("800000000000000000000000000000015F700CFFF1A624E5E497161BCC8A198F")), + a=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000C96")), + b=bytes2long(hexdec("3E1AF419A269A5F866A7D3C25C3DF80AE979259373FF2B182F49D4CE7E1BBC8B")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000001")), + y=bytes2long(hexdec("3FA8124359F96680B83D1C3EB2C070E5C545C9858D03ECFB744BF8D717717EFC")), + ), + "id-tc26-gost-3410-12-256-paramSetD": GOST3410Curve( + p=bytes2long(hexdec("9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D759B")), + q=bytes2long(hexdec("9B9F605F5A858107AB1EC85E6B41C8AA582CA3511EDDFB74F02F3A6598980BB9")), + a=bytes2long(hexdec("9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D7598")), + b=bytes2long(hexdec("000000000000000000000000000000000000000000000000000000000000805a")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000000")), + y=bytes2long(hexdec("41ECE55743711A8C3CBF3783CD08C0EE4D4DC440D4641A8F366E550DFDB3BB67")), + ), + "id-tc26-gost-3410-12-512-paramSetTest": GOST3410Curve( + p=bytes2long(hexdec("4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DF1D852741AF4704A0458047E80E4546D35B8336FAC224DD81664BBF528BE6373")), + q=bytes2long(hexdec("4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DA82F2D7ECB1DBAC719905C5EECC423F1D86E25EDBE23C595D644AAF187E6E6DF")), + a=7, + b=bytes2long(hexdec("1CFF0806A31116DA29D8CFA54E57EB748BC5F377E49400FDD788B649ECA1AC4361834013B2AD7322480A89CA58E0CF74BC9E540C2ADD6897FAD0A3084F302ADC")), + x=bytes2long(hexdec("24D19CC64572EE30F396BF6EBBFD7A6C5213B3B3D7057CC825F91093A68CD762FD60611262CD838DC6B60AA7EEE804E28BC849977FAC33B4B530F1B120248A9A")), + y=bytes2long(hexdec("2BB312A43BD2CE6E0D020613C857ACDDCFBF061E91E5F2C3F32447C259F39B2C83AB156D77F1496BF7EB3351E1EE4E43DC1A18B91B24640B6DBB92CB1ADD371E")), + ), + "id-tc26-gost-3410-12-512-paramSetA": GOST3410Curve( + p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7")), + q=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275")), + a=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4")), + b=bytes2long(hexdec("E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760")), + x=bytes2long(hexdec("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003")), + y=bytes2long(hexdec("7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4")), + ), + "id-tc26-gost-3410-12-512-paramSetB": GOST3410Curve( + p=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F")), + q=bytes2long(hexdec("800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD")), + a=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C")), + b=bytes2long(hexdec("687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116")), + x=bytes2long(hexdec("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002")), + y=bytes2long(hexdec("1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD")), + ), + "id-tc26-gost-3410-12-512-paramSetC": GOST3410Curve( + p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7")), + q=bytes2long(hexdec("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC98CDBA46506AB004C33A9FF5147502CC8EDA9E7A769A12694623CEF47F023ED")), + a=bytes2long(hexdec("DC9203E514A721875485A529D2C722FB187BC8980EB866644DE41C68E143064546E861C0E2C9EDD92ADE71F46FCF50FF2AD97F951FDA9F2A2EB6546F39689BD3")), + b=bytes2long(hexdec("B4C4EE28CEBC6C2C8AC12952CF37F16AC7EFB6A9F69F4B57FFDA2E4F0DE5ADE038CBC2FFF719D2C18DE0284B8BFEF3B52B8CC7A5F5BF0A3C8D2319A5312557E1")), + x=bytes2long(hexdec("E2E31EDFC23DE7BDEBE241CE593EF5DE2295B7A9CBAEF021D385F7074CEA043AA27272A7AE602BF2A7B9033DB9ED3610C6FB85487EAE97AAC5BC7928C1950148")), + y=bytes2long(hexdec("F5CE40D95B5EB899ABBCCFF5911CB8577939804D6527378B8C108C3D2090FF9BE18E2D33E3021ED2EF32D85822423B6304F726AA854BAE07D0396E9A9ADDC40F")), + cofactor=4, + e=0x01, + d=bytes2long(hexdec("9E4F5D8C017D8D9F13A5CF3CDF5BFE4DAB402D54198E31EBDE28A0621050439CA6B39E0A515C06B304E2CE43E79E369E91A0CFC2BC2A22B4CA302DBB33EE7550")), + ), +} +CURVES["id-GostR3410-2001-CryptoPro-A-ParamSet"] = CURVES["id-tc26-gost-3410-12-256-paramSetB"] +CURVES["id-GostR3410-2001-CryptoPro-B-ParamSet"] = CURVES["id-tc26-gost-3410-12-256-paramSetC"] +CURVES["id-GostR3410-2001-CryptoPro-C-ParamSet"] = CURVES["id-tc26-gost-3410-12-256-paramSetD"] +CURVES["id-GostR3410-2001-CryptoPro-XchA-ParamSet"] = CURVES["id-GostR3410-2001-CryptoPro-A-ParamSet"] +CURVES["id-GostR3410-2001-CryptoPro-XchB-ParamSet"] = CURVES["id-GostR3410-2001-CryptoPro-C-ParamSet"] +CURVES["id-tc26-gost-3410-2012-256-paramSetA"] = CURVES["id-tc26-gost-3410-12-256-paramSetA"] +CURVES["id-tc26-gost-3410-2012-256-paramSetB"] = CURVES["id-tc26-gost-3410-12-256-paramSetB"] +CURVES["id-tc26-gost-3410-2012-256-paramSetC"] = CURVES["id-tc26-gost-3410-12-256-paramSetC"] +CURVES["id-tc26-gost-3410-2012-256-paramSetD"] = CURVES["id-tc26-gost-3410-12-256-paramSetD"] +CURVES["id-tc26-gost-3410-2012-512-paramSetTest"] = CURVES["id-tc26-gost-3410-12-512-paramSetTest"] +CURVES["id-tc26-gost-3410-2012-512-paramSetA"] = CURVES["id-tc26-gost-3410-12-512-paramSetA"] +CURVES["id-tc26-gost-3410-2012-512-paramSetB"] = CURVES["id-tc26-gost-3410-12-512-paramSetB"] +CURVES["id-tc26-gost-3410-2012-512-paramSetC"] = CURVES["id-tc26-gost-3410-12-512-paramSetC"] +for _name, _curve in CURVES.items(): + _curve.name = _name +DEFAULT_CURVE = CURVES["id-tc26-gost-3410-12-256-paramSetB"] + + +def public_key(curve, prv, mask=None): + """Generate public key from the private one + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :returns: public key's parts, X and Y + :rtype: (long, long) + """ + pub = curve.exp(prv) + if mask is not None: + pub = curve.exp(mask, pub[0], pub[1]) + return pub + + +def sign(curve, prv, digest, rand=None, mask=None): + """Calculate signature for provided digest + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param digest: digest for signing + :type digest: bytes, 32 or 64 bytes + :param rand: optional predefined random data used for k/r generation + :type rand: bytes, 32 or 64 bytes + :returns: signature, BE(S) || BE(R) + :rtype: bytes, 64 or 128 bytes + """ + size = curve.point_size + q = curve.q + e = bytes2long(digest) % q + if e == 0: + e = 1 + while True: + if rand is None: + rand = urandom(size) + elif len(rand) != size: + raise ValueError("rand length != %d" % size) + k = bytes2long(rand) % q + if k == 0: + continue + r, y = curve.exp(k) + if mask is not None: + r, y = curve.exp(mask, x=r, y=y) + r %= q + if r == 0: + continue + d = prv * r + k *= e + s = d + k + if mask is not None: + s *= mask + s %= q + if s == 0: + continue + break + return long2bytes(s, size) + long2bytes(r, size) + + +def verify(curve, pub, digest, signature): + """Verify provided digest with the signature + + :param GOST3410Curve curve: curve to use + :type pub: (long, long) + :param digest: digest needed to check + :type digest: bytes, 32 or 64 bytes + :param signature: signature to verify with + :type signature: bytes, 64 or 128 bytes + :rtype: bool + """ + size = curve.point_size + if len(signature) != size * 2: + raise ValueError("Invalid signature length") + q = curve.q + p = curve.p + s = bytes2long(signature[:size]) + r = bytes2long(signature[size:]) + if r <= 0 or r >= q or s <= 0 or s >= q: + return False + e = bytes2long(digest) % curve.q + if e == 0: + e = 1 + v = modinvert(e, q) + z1 = s * v % q + z2 = q - r * v % q + p1x, p1y = curve.exp(z1) + q1x, q1y = curve.exp(z2, pub[0], pub[1]) + lm = q1x - p1x + if lm < 0: + lm += p + lm = modinvert(lm, p) + z1 = q1y - p1y + lm = lm * z1 % p + lm = lm * lm % p + lm = lm - p1x - q1x + lm = lm % p + if lm < 0: + lm += p + lm %= q + # This is not constant time comparison! + return lm == r + + +def prv_unmarshal(prv): + """Unmarshal little-endian private key + + :param bytes prv: serialized private key + :rtype: long + + It is advisable to use :py:func:`pygost.gost3410.prv_marshal` to + assure that key i in curve's Q field for better compatibility with + some implementations. + """ + return bytes2long(prv[::-1]) + + +def prv_marshal(curve, prv): + """Marshal little-endian private key + + :param GOST3410Curve curve: curve to use + :param long prv: serialized private key + :rtype: bytes + + Key is in curve's Q field. + """ + return long2bytes(prv % curve.q, point_size(prv))[::-1] + + +def pub_marshal(pub): + """Marshal public key + + :type pub: (long, long) + :rtype: bytes + :returns: LE(X) || LE(Y) + """ + size = point_size(pub[0]) + return (long2bytes(pub[1], size) + long2bytes(pub[0], size))[::-1] + + +def pub_unmarshal(pub): + """Unmarshal public key + + :param pub: LE(X) || LE(Y) + :type pub: bytes + :rtype: (long, long) + """ + size = len(pub) // 2 + pub = pub[::-1] + return (bytes2long(pub[size:]), bytes2long(pub[:size])) + + +def uv2xy(curve, u, v): + """Convert twisted Edwards curve U,V coordinates to Weierstrass X,Y + """ + s, t = curve.st() + k1 = (s * (1 + v)) % curve.p + k2 = curve.pos(1 - v) + x = t + k1 * modinvert(k2, curve.p) + y = k1 * modinvert(u * k2, curve.p) + return x % curve.p, y % curve.p + + +def xy2uv(curve, x, y): + """Convert Weierstrass X,Y coordinates to twisted Edwards curve U,V + """ + s, t = curve.st() + xmt = curve.pos(x - t) + u = xmt * modinvert(y, curve.p) + v = curve.pos(xmt - s) * modinvert(xmt + s, curve.p) + return u % curve.p, v % curve.p diff --git a/pygost-5.13/build/lib/pygost/gost3410_vko.py b/pygost-5.13/build/lib/pygost/gost3410_vko.py new file mode 100644 index 0000000..3f169dc --- /dev/null +++ b/pygost-5.13/build/lib/pygost/gost3410_vko.py @@ -0,0 +1,97 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Key agreement functions, VKO GOST R 34.10-2001/2012 +""" + +from pygost.gost3410 import pub_marshal +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.gost341194 import GOST341194 +from pygost.utils import bytes2long + + +def ukm_unmarshal(ukm): + """Unmarshal UKM value + + :type ukm: little-endian bytes + :rtype: long + """ + return bytes2long(ukm[::-1]) + + +def kek(curve, prv, pub, ukm, mask=None): + if not curve.contains(pub): + raise ValueError("pub is not on the curve") + key = curve.exp(prv, pub[0], pub[1]) + key = curve.exp(curve.cofactor * ukm, key[0], key[1]) + if mask is not None: + key = curve.exp(mask, key[0], key[1]) + return pub_marshal(key) + + +def kek_34102001(curve, prv, pub, ukm): + """Key agreement (34.10-2001, 34.11-94) + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param pub: public key + :type pub: (long, long) + :param long ukm: user keying material, VKO-factor + :returns: Key Encryption Key (shared key) + :rtype: bytes, 32 bytes + + Shared Key Encryption Key computation is based on + :rfc:`4357` VKO GOST R 34.10-2001 with little-endian + hash output. + """ + return GOST341194( + kek(curve, prv, pub, ukm), + sbox="id-GostR3411-94-CryptoProParamSet", + ).digest() + + +def kek_34102012256(curve, prv, pub, ukm=1): + """Key agreement (34.10-2012, 34.11-2012 256 bit) + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param pub: public key + :type pub: (long, long) + :param long ukm: user keying material, VKO-factor + :returns: Key Encryption Key (shared key) + :rtype: bytes, 32 bytes + + Shared Key Encryption Key computation is based on + :rfc:`7836` VKO GOST R 34.10-2012. + """ + return GOST34112012256(kek(curve, prv, pub, ukm)).digest() + + +def kek_34102012512(curve, prv, pub, ukm=1): + """Key agreement (34.10-2012, 34.11-2012 512 bit) + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param pub: public key + :type pub: (long, long) + :param long ukm: user keying material, VKO-factor + :returns: Key Encryption Key (shared key) + :rtype: bytes, 32 bytes + + Shared Key Encryption Key computation is based on + :rfc:`7836` VKO GOST R 34.10-2012. + """ + return GOST34112012512(kek(curve, prv, pub, ukm)).digest() diff --git a/pygost-5.13/build/lib/pygost/gost34112012.py b/pygost-5.13/build/lib/pygost/gost34112012.py new file mode 100644 index 0000000..91782de --- /dev/null +++ b/pygost-5.13/build/lib/pygost/gost34112012.py @@ -0,0 +1,299 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST R 34.11-2012 (Streebog) hash function common files + +This is implementation of :rfc:`6986`. Most function and variable names are +taken according to specification's terminology. +""" + +from copy import copy +from struct import pack +from struct import unpack + +from pygost.iface import PEP247 +from pygost.utils import hexdec +from pygost.utils import strxor +from pygost.utils import xrange + + +BLOCKSIZE = 64 +Pi = bytearray(( + 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, + 218, 35, 197, 4, 77, 233, 119, 240, 219, 147, 46, + 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, 249, + 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, + 139, 1, 142, 79, 5, 132, 2, 174, 227, 106, 143, + 160, 6, 11, 237, 152, 127, 212, 211, 31, 235, 52, + 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, + 58, 206, 204, 181, 112, 14, 86, 8, 12, 118, 18, + 191, 114, 19, 71, 156, 183, 93, 135, 21, 161, 150, + 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, + 178, 177, 50, 117, 25, 61, 255, 53, 138, 126, 109, + 84, 198, 128, 195, 189, 13, 87, 223, 245, 36, 169, + 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, + 3, 224, 15, 236, 222, 122, 148, 176, 188, 220, 232, + 40, 80, 78, 51, 10, 74, 167, 151, 96, 115, 30, + 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, + 173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, + 125, 105, 213, 149, 59, 7, 88, 179, 64, 134, 172, + 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, 225, + 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, + 202, 216, 133, 97, 32, 113, 103, 164, 45, 43, 9, + 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166, + 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, + 75, 99, 182, +)) + +A = [unpack(">Q", hexdec(s))[0] for s in ( + "8e20faa72ba0b470", "47107ddd9b505a38", "ad08b0e0c3282d1c", "d8045870ef14980e", + "6c022c38f90a4c07", "3601161cf205268d", "1b8e0b0e798c13c8", "83478b07b2468764", + "a011d380818e8f40", "5086e740ce47c920", "2843fd2067adea10", "14aff010bdd87508", + "0ad97808d06cb404", "05e23c0468365a02", "8c711e02341b2d01", "46b60f011a83988e", + "90dab52a387ae76f", "486dd4151c3dfdb9", "24b86a840e90f0d2", "125c354207487869", + "092e94218d243cba", "8a174a9ec8121e5d", "4585254f64090fa0", "accc9ca9328a8950", + "9d4df05d5f661451", "c0a878a0a1330aa6", "60543c50de970553", "302a1e286fc58ca7", + "18150f14b9ec46dd", "0c84890ad27623e0", "0642ca05693b9f70", "0321658cba93c138", + "86275df09ce8aaa8", "439da0784e745554", "afc0503c273aa42a", "d960281e9d1d5215", + "e230140fc0802984", "71180a8960409a42", "b60c05ca30204d21", "5b068c651810a89e", + "456c34887a3805b9", "ac361a443d1c8cd2", "561b0d22900e4669", "2b838811480723ba", + "9bcf4486248d9f5d", "c3e9224312c8c1a0", "effa11af0964ee50", "f97d86d98a327728", + "e4fa2054a80b329c", "727d102a548b194e", "39b008152acb8227", "9258048415eb419d", + "492c024284fbaec0", "aa16012142f35760", "550b8e9e21f7a530", "a48b474f9ef5dc18", + "70a6a56e2440598e", "3853dc371220a247", "1ca76e95091051ad", "0edd37c48a08a6d8", + "07e095624504536c", "8d70c431ac02a736", "c83862965601dd1b", "641c314b2b8ee083", +)] + +Tau = ( + 0, 8, 16, 24, 32, 40, 48, 56, + 1, 9, 17, 25, 33, 41, 49, 57, + 2, 10, 18, 26, 34, 42, 50, 58, + 3, 11, 19, 27, 35, 43, 51, 59, + 4, 12, 20, 28, 36, 44, 52, 60, + 5, 13, 21, 29, 37, 45, 53, 61, + 6, 14, 22, 30, 38, 46, 54, 62, + 7, 15, 23, 31, 39, 47, 55, 63, +) + +C = [hexdec("".join(s))[::-1] for s in ( + ( + "b1085bda1ecadae9ebcb2f81c0657c1f", + "2f6a76432e45d016714eb88d7585c4fc", + "4b7ce09192676901a2422a08a460d315", + "05767436cc744d23dd806559f2a64507", + ), + ( + "6fa3b58aa99d2f1a4fe39d460f70b5d7", + "f3feea720a232b9861d55e0f16b50131", + "9ab5176b12d699585cb561c2db0aa7ca", + "55dda21bd7cbcd56e679047021b19bb7", + ), + ( + "f574dcac2bce2fc70a39fc286a3d8435", + "06f15e5f529c1f8bf2ea7514b1297b7b", + "d3e20fe490359eb1c1c93a376062db09", + "c2b6f443867adb31991e96f50aba0ab2", + ), + ( + "ef1fdfb3e81566d2f948e1a05d71e4dd", + "488e857e335c3c7d9d721cad685e353f", + "a9d72c82ed03d675d8b71333935203be", + "3453eaa193e837f1220cbebc84e3d12e", + ), + ( + "4bea6bacad4747999a3f410c6ca92363", + "7f151c1f1686104a359e35d7800fffbd", + "bfcd1747253af5a3dfff00b723271a16", + "7a56a27ea9ea63f5601758fd7c6cfe57", + ), + ( + "ae4faeae1d3ad3d96fa4c33b7a3039c0", + "2d66c4f95142a46c187f9ab49af08ec6", + "cffaa6b71c9ab7b40af21f66c2bec6b6", + "bf71c57236904f35fa68407a46647d6e", + ), + ( + "f4c70e16eeaac5ec51ac86febf240954", + "399ec6c7e6bf87c9d3473e33197a93c9", + "0992abc52d822c3706476983284a0504", + "3517454ca23c4af38886564d3a14d493", + ), + ( + "9b1f5b424d93c9a703e7aa020c6e4141", + "4eb7f8719c36de1e89b4443b4ddbc49a", + "f4892bcb929b069069d18d2bd1a5c42f", + "36acc2355951a8d9a47f0dd4bf02e71e", + ), + ( + "378f5a541631229b944c9ad8ec165fde", + "3a7d3a1b258942243cd955b7e00d0984", + "800a440bdbb2ceb17b2b8a9aa6079c54", + "0e38dc92cb1f2a607261445183235adb", + ), + ( + "abbedea680056f52382ae548b2e4f3f3", + "8941e71cff8a78db1fffe18a1b336103", + "9fe76702af69334b7a1e6c303b7652f4", + "3698fad1153bb6c374b4c7fb98459ced", + ), + ( + "7bcd9ed0efc889fb3002c6cd635afe94", + "d8fa6bbbebab07612001802114846679", + "8a1d71efea48b9caefbacd1d7d476e98", + "dea2594ac06fd85d6bcaa4cd81f32d1b", + ), + ( + "378ee767f11631bad21380b00449b17a", + "cda43c32bcdf1d77f82012d430219f9b", + "5d80ef9d1891cc86e71da4aa88e12852", + "faf417d5d9b21b9948bc924af11bd720", + ), +)] + + +def _lcache(): + cache = [] + for byteN in xrange(8): + cache.append([0 for _ in xrange(256)]) + for byteN in xrange(8): + for byteVal in xrange(256): + res64 = 0 + val = byteVal + for bitN in xrange(8): + if val & 0x80 > 0: + res64 ^= A[(7 - byteN) * 8 + bitN] + val <<= 1 + cache[byteN][byteVal] = res64 + return cache + + +# Trade memory for CPU for part of L() calculations +LCache = _lcache() + + +def add512bit(a, b): + a = int.from_bytes(a, "little") + b = int.from_bytes(b, "little") + r = (a + b) % (1 << 512) + return r.to_bytes(512 // 8, "little") + + +def g(n, hsh, msg): + res = E(LPS(strxor(hsh[:8], pack(">> m = GOST34112012(digest_size=32) + >>> m.update("foo") + >>> m.update("bar") + >>> m.hexdigest() + 'e3c9fd89226d93b489a9fe27d686806e24a514e3787bca053c698ec4616ceb78' + """ + block_size = BLOCKSIZE + + def __init__(self, data=b"", digest_size=64): + """ + :param digest_size: hash digest size to compute + :type digest_size: 32 or 64 bytes + """ + self._digest_size = digest_size + self.hsh = BLOCKSIZE * (b"\x01" if digest_size == 32 else b"\x00") + self.chk = bytearray(BLOCKSIZE * b"\x00") + self.n = 0 + self.buf = b"" + self.update(data) + + def copy(self): + obj = GOST34112012() + obj._digest_size = self._digest_size + obj.hsh = self.hsh + obj.chk = copy(self.chk) + obj.n = self.n + obj.buf = self.buf + return obj + + @property + def digest_size(self): + return self._digest_size + + def _update_block(self, block): + self.hsh = g(self.n, self.hsh, block) + self.chk = add512bit(self.chk, block) + self.n += 512 + + def update(self, data): + """Update state with the new data + """ + if len(self.buf) > 0: + chunk_len = BLOCKSIZE - len(self.buf) + self.buf += data[:chunk_len] + data = data[chunk_len:] + if len(self.buf) == BLOCKSIZE: + self._update_block(self.buf) + self.buf = b"" + while len(data) >= BLOCKSIZE: + self._update_block(data[:BLOCKSIZE]) + data = data[BLOCKSIZE:] + self.buf += data + + def digest(self): + """Get hash of the provided data + """ + data = self.buf + + # Padding + padblock_size = len(data) * 8 + data += b"\x01" + padlen = BLOCKSIZE - len(data) + if padlen != BLOCKSIZE: + data += b"\x00" * padlen + + hsh = g(self.n, self.hsh, data) + n = self.n + padblock_size + chk = add512bit(self.chk, data) + hsh = g(0, hsh, pack(" +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST R 34.11-94 hash function + +This is implementation of :rfc:`5831`. Most function and variable names are +taken according to specification's terminology. +""" + +from copy import copy +from functools import partial +from struct import pack + +from pygost.gost28147 import block2ns +from pygost.gost28147 import encrypt +from pygost.gost28147 import ns2block +from pygost.gost28147 import validate_sbox +from pygost.iface import PEP247 +from pygost.pbkdf2 import pbkdf2 as pbkdf2_base +from pygost.utils import hexdec +from pygost.utils import hexenc +from pygost.utils import strxor +from pygost.utils import xrange + + +DEFAULT_SBOX = "id-GostR3411-94-CryptoProParamSet" +BLOCKSIZE = 32 +C2 = 32 * b"\x00" +C3 = hexdec(b"ff00ffff000000ffff0000ff00ffff0000ff00ff00ff00ffff00ff00ff00ff00") +C4 = 32 * b"\x00" +digest_size = 32 + + +def A(x): + x4, x3, x2, x1 = x[0:8], x[8:16], x[16:24], x[24:32] + return b"".join((strxor(x1, x2), x4, x3, x2)) + + +def P(x): + return bytearray(( + x[0], x[8], x[16], x[24], x[1], x[9], x[17], x[25], x[2], + x[10], x[18], x[26], x[3], x[11], x[19], x[27], x[4], x[12], + x[20], x[28], x[5], x[13], x[21], x[29], x[6], x[14], x[22], + x[30], x[7], x[15], x[23], x[31], + )) + + +def _chi(Y): + """Chi function + + This is some kind of LFSR. + """ + (y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2, y1) = ( + Y[0:2], Y[2:4], Y[4:6], Y[6:8], Y[8:10], Y[10:12], Y[12:14], + Y[14:16], Y[16:18], Y[18:20], Y[20:22], Y[22:24], Y[24:26], + Y[26:28], Y[28:30], Y[30:32], + ) + by1, by2, by3, by4, by13, by16, byx = ( + bytearray(y1), bytearray(y2), bytearray(y3), bytearray(y4), + bytearray(y13), bytearray(y16), bytearray(2), + ) + byx[0] = by1[0] ^ by2[0] ^ by3[0] ^ by4[0] ^ by13[0] ^ by16[0] + byx[1] = by1[1] ^ by2[1] ^ by3[1] ^ by4[1] ^ by13[1] ^ by16[1] + return b"".join(( + bytes(byx), y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2 + )) + + +def _step(hin, m, sbox): + """Step function + + H_out = f(H_in, m) + """ + # Generate keys + u = hin + v = m + w = strxor(hin, m) + k1 = P(w) + + u = strxor(A(u), C2) + v = A(A(v)) + w = strxor(u, v) + k2 = P(w) + + u = strxor(A(u), C3) + v = A(A(v)) + w = strxor(u, v) + k3 = P(w) + + u = strxor(A(u), C4) + v = A(A(v)) + w = strxor(u, v) + k4 = P(w) + + # Encipher + h4, h3, h2, h1 = hin[0:8], hin[8:16], hin[16:24], hin[24:32] + s1 = ns2block(encrypt(sbox, k1[::-1], block2ns(h1[::-1])))[::-1] + s2 = ns2block(encrypt(sbox, k2[::-1], block2ns(h2[::-1])))[::-1] + s3 = ns2block(encrypt(sbox, k3[::-1], block2ns(h3[::-1])))[::-1] + s4 = ns2block(encrypt(sbox, k4[::-1], block2ns(h4[::-1])))[::-1] + s = b"".join((s4, s3, s2, s1)) + + # Permute + # H_out = chi^61(H_in XOR chi(m XOR chi^12(S))) + x = s + for _ in xrange(12): + x = _chi(x) + x = strxor(x, m) + x = _chi(x) + x = strxor(hin, x) + for _ in xrange(61): + x = _chi(x) + return x + + +class GOST341194(PEP247): + """GOST 34.11-94 big-endian hash + + >>> m = GOST341194() + >>> m.update("foo") + >>> m.update("bar") + >>> m.hexdigest() + '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af' + >>> GOST341194("foobar").hexdigest() + '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af' + """ + block_size = BLOCKSIZE + digest_size = digest_size + + def __init__(self, data=b"", sbox=DEFAULT_SBOX): + """ + :param bytes data: provide initial data + :param bytes sbox: S-box to use + """ + validate_sbox(sbox) + self.data = data + self.sbox = sbox + + def copy(self): + return GOST341194(copy(self.data), self.sbox) + + def update(self, data): + """Append data that has to be hashed + """ + self.data += data + + def digest(self): + """Get hash of the provided data + """ + _len = 0 + checksum = 0 + h = 32 * b"\x00" + m = self.data + for i in xrange(0, len(m), BLOCKSIZE): + part = m[i:i + BLOCKSIZE][::-1] + _len += len(part) * 8 + checksum = (checksum + int(hexenc(part), 16)) % (2 ** 256) + if len(part) < BLOCKSIZE: + part = b"\x00" * (BLOCKSIZE - len(part)) + part + h = _step(h, part, self.sbox) + h = _step(h, 24 * b"\x00" + pack(">Q", _len), self.sbox) + + checksum = hex(checksum)[2:].rstrip("L") + if len(checksum) % 2 != 0: + checksum = "0" + checksum + checksum = hexdec(checksum) + checksum = b"\x00" * (BLOCKSIZE - len(checksum)) + checksum + h = _step(h, checksum, self.sbox) + return h[::-1] + + +def new(data=b"", sbox=DEFAULT_SBOX): + return GOST341194(data, sbox) + + +PBKDF2_HASHER = partial(GOST341194, sbox="id-GostR3411-94-CryptoProParamSet") + + +def pbkdf2(password, salt, iterations, dklen): + return pbkdf2_base(PBKDF2_HASHER, password, salt, iterations, dklen) diff --git a/pygost-5.13/build/lib/pygost/gost3412.py b/pygost-5.13/build/lib/pygost/gost3412.py new file mode 100644 index 0000000..b9472ee --- /dev/null +++ b/pygost-5.13/build/lib/pygost/gost3412.py @@ -0,0 +1,186 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST 34.12-2015 64 and 128 bit block ciphers (:rfc:`7801`) + +Several precalculations are performed during this module importing. +""" + +from pygost.gost28147 import block2ns as gost28147_block2ns +from pygost.gost28147 import decrypt as gost28147_decrypt +from pygost.gost28147 import encrypt as gost28147_encrypt +from pygost.gost28147 import ns2block as gost28147_ns2block +from pygost.utils import strxor +from pygost.utils import xrange + + +KEYSIZE = 32 + +LC = bytearray(( + 148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148, 1, +)) +PI = bytearray(( + 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77, + 233, 119, 240, 219, 147, 46, 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, + 249, 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, 139, 1, 142, 79, 5, + 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127, 212, 211, 31, 235, + 52, 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, 58, 206, 204, 181, + 112, 14, 86, 8, 12, 118, 18, 191, 114, 19, 71, 156, 183, 93, 135, 21, 161, + 150, 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, 178, 177, 50, 117, + 25, 61, 255, 53, 138, 126, 109, 84, 198, 128, 195, 189, 13, 87, 223, 245, + 36, 169, 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, 3, 224, 15, + 236, 222, 122, 148, 176, 188, 220, 232, 40, 80, 78, 51, 10, 74, 167, 151, + 96, 115, 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, 173, 69, 70, + 146, 39, 94, 85, 47, 140, 163, 165, 125, 105, 213, 149, 59, 7, 88, 179, 64, + 134, 172, 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, 225, 27, 131, 73, + 76, 63, 248, 254, 141, 83, 170, 144, 202, 216, 133, 97, 32, 113, 103, 164, + 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166, 116, 210, 230, + 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182, +)) + +######################################################################## +# Precalculate inverted PI value as a performance optimization. +# Actually it can be computed only once and saved on the disk. +######################################################################## +PIinv = bytearray(256) +for x in xrange(256): + PIinv[PI[x]] = x + + +def gf(a, b): + c = 0 + while b: + if b & 1: + c ^= a + if a & 0x80: + a = (a << 1) ^ 0x1C3 + else: + a <<= 1 + b >>= 1 + return c + +######################################################################## +# Precalculate all possible gf(byte, byte) values as a performance +# optimization. +# Actually it can be computed only once and saved on the disk. +######################################################################## + + +GF = [bytearray(256) for _ in xrange(256)] + +for x in xrange(256): + for y in xrange(256): + GF[x][y] = gf(x, y) + + +def L(blk, rounds=16): + for _ in range(rounds): + t = blk[15] + for i in range(14, -1, -1): + blk[i + 1] = blk[i] + t ^= GF[blk[i]][LC[i]] + blk[0] = t + return blk + + +def Linv(blk): + for _ in range(16): + t = blk[0] + for i in range(15): + blk[i] = blk[i + 1] + t ^= GF[blk[i]][LC[i]] + blk[15] = t + return blk + +######################################################################## +# Precalculate values of the C -- it does not depend on key. +# Actually it can be computed only once and saved on the disk. +######################################################################## + + +C = [] + +for x in range(1, 33): + y = bytearray(16) + y[15] = x + C.append(L(y)) + + +def lp(blk): + return L([PI[v] for v in blk]) + + +class GOST3412Kuznechik(object): + """GOST 34.12-2015 128-bit block cipher Кузнечик (Kuznechik) + """ + blocksize = 16 + + def __init__(self, key): + """ + :param key: encryption/decryption key + :type key: bytes, 32 bytes + + Key scheduling (roundkeys precomputation) is performed here. + """ + kr0 = bytearray(key[:16]) + kr1 = bytearray(key[16:]) + self.ks = [kr0, kr1] + for i in range(4): + for j in range(8): + k = lp(bytearray(strxor(C[8 * i + j], kr0))) + kr0, kr1 = [strxor(k, kr1), kr0] + self.ks.append(kr0) + self.ks.append(kr1) + + def encrypt(self, blk): + blk = bytearray(blk) + for i in range(9): + blk = lp(bytearray(strxor(self.ks[i], blk))) + return bytes(strxor(self.ks[9], blk)) + + def decrypt(self, blk): + blk = bytearray(blk) + for i in range(9, 0, -1): + blk = [PIinv[v] for v in Linv(bytearray(strxor(self.ks[i], blk)))] + return bytes(strxor(self.ks[0], blk)) + + +class GOST3412Magma(object): + """GOST 34.12-2015 64-bit block cipher Магма (Magma) + """ + blocksize = 8 + + def __init__(self, key): + """ + :param key: encryption/decryption key + :type key: bytes, 32 bytes + """ + # Backward compatibility key preparation for 28147-89 key schedule + self.key = b"".join(key[i * 4:i * 4 + 4][::-1] for i in range(8)) + self.sbox = "id-tc26-gost-28147-param-Z" + + def encrypt(self, blk): + return gost28147_ns2block(gost28147_encrypt( + self.sbox, + self.key, + gost28147_block2ns(blk[::-1]), + ))[::-1] + + def decrypt(self, blk): + return gost28147_ns2block(gost28147_decrypt( + self.sbox, + self.key, + gost28147_block2ns(blk[::-1]), + ))[::-1] diff --git a/pygost-5.13/build/lib/pygost/gost3413.py b/pygost-5.13/build/lib/pygost/gost3413.py new file mode 100644 index 0000000..f3cb5da --- /dev/null +++ b/pygost-5.13/build/lib/pygost/gost3413.py @@ -0,0 +1,392 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST R 34.13-2015: Modes of operation for block ciphers + +This module currently includes only padding methods. +""" + +from os import urandom + +from pygost.utils import bytes2long +from pygost.utils import long2bytes +from pygost.utils import strxor +from pygost.utils import xrange + + +KEYSIZE = 32 + + +def pad_size(data_size, blocksize): + """Calculate required pad size to full up blocksize + """ + if data_size < blocksize: + return blocksize - data_size + if data_size % blocksize == 0: + return 0 + return blocksize - data_size % blocksize + + +def pad1(data, blocksize): + """Padding method 1 + + Just fill up with zeros if necessary. + """ + return data + b"\x00" * pad_size(len(data), blocksize) + + +def pad2(data, blocksize): + """Padding method 2 (also known as ISO/IEC 7816-4) + + Add one bit and then fill up with zeros. + """ + return data + b"\x80" + b"\x00" * pad_size(len(data) + 1, blocksize) + + +def unpad2(data, blocksize): + """Unpad method 2 + """ + last_block = bytearray(data[-blocksize:]) + pad_index = last_block.rfind(b"\x80") + if pad_index == -1: + raise ValueError("Invalid padding") + for c in last_block[pad_index + 1:]: + if c != 0: + raise ValueError("Invalid padding") + return data[:-(blocksize - pad_index)] + + +def pad3(data, blocksize): + """Padding method 3 + """ + if pad_size(len(data), blocksize) == 0: + return data + return pad2(data, blocksize) + + +def ecb_encrypt(encrypter, bs, pt): + """ECB encryption mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes pt: already padded plaintext + """ + if not pt or len(pt) % bs != 0: + raise ValueError("Plaintext is not blocksize aligned") + ct = [] + for i in xrange(0, len(pt), bs): + ct.append(encrypter(pt[i:i + bs])) + return b"".join(ct) + + +def ecb_decrypt(decrypter, bs, ct): + """ECB decryption mode of operation + + :param decrypter: Decrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes ct: ciphertext + """ + if not ct or len(ct) % bs != 0: + raise ValueError("Ciphertext is not blocksize aligned") + pt = [] + for i in xrange(0, len(ct), bs): + pt.append(decrypter(ct[i:i + bs])) + return b"".join(pt) + + +def acpkm(encrypter, bs): + """Perform ACPKM key derivation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + """ + return b"".join([ + encrypter(bytes(bytearray(range(d, d + bs)))) + for d in range(0x80, 0x80 + bs * (KEYSIZE // bs), bs) + ]) + + +def ctr(encrypter, bs, data, iv, _acpkm=None): + """Counter mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes data: plaintext/ciphertext + :param bytes iv: half blocksize-sized initialization vector + + For decryption you use the same function again. + """ + if len(iv) != bs // 2: + raise ValueError("Invalid IV size") + if len(data) > bs * (1 << (8 * (bs // 2 - 1))): + raise ValueError("Too big data") + stream = [] + ctr_value = 0 + ctr_max_value = 1 << (8 * (bs // 2)) + if _acpkm is not None: + acpkm_algo_class, acpkm_section_size_in_bs = _acpkm + acpkm_section_size_in_bs //= bs + for _ in xrange(0, len(data) + pad_size(len(data), bs), bs): + if ( + _acpkm is not None and + ctr_value != 0 and + ctr_value % acpkm_section_size_in_bs == 0 + ): + encrypter = acpkm_algo_class(acpkm(encrypter, bs)).encrypt + stream.append(encrypter(iv + long2bytes(ctr_value, bs // 2))) + ctr_value = (ctr_value + 1) % ctr_max_value + return strxor(b"".join(stream), data) + + +def ctr_acpkm(algo_class, encrypter, section_size, bs, data, iv): + """CTR-ACPKM mode of operation + + :param algo_class: pygost.gost3412's algorithm class + :param encrypter: encrypting function, that takes block as an input + :param int section_size: ACPKM'es section size (N), in bytes + :param int bs: cipher's blocksize, bytes + :param bytes data: plaintext/ciphertext + :param bytes iv: half blocksize-sized initialization vector + + For decryption you use the same function again. + """ + if section_size % bs != 0: + raise ValueError("section_size must be multiple of bs") + return ctr(encrypter, bs, data, iv, _acpkm=(algo_class, section_size)) + + +def ofb(encrypter, bs, data, iv): + """OFB mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes data: plaintext/ciphertext + :param bytes iv: blocksize-sized initialization vector + + For decryption you use the same function again. + """ + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + result = [] + for i in xrange(0, len(data) + pad_size(len(data), bs), bs): + r = r[1:] + [encrypter(r[0])] + result.append(strxor(r[-1], data[i:i + bs])) + return b"".join(result) + + +def cbc_encrypt(encrypter, bs, pt, iv): + """CBC encryption mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes pt: already padded plaintext + :param bytes iv: blocksize-sized initialization vector + """ + if not pt or len(pt) % bs != 0: + raise ValueError("Plaintext is not blocksize aligned") + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + ct = [] + for i in xrange(0, len(pt), bs): + ct.append(encrypter(strxor(r[0], pt[i:i + bs]))) + r = r[1:] + [ct[-1]] + return b"".join(ct) + + +def cbc_decrypt(decrypter, bs, ct, iv): + """CBC decryption mode of operation + + :param decrypter: Decrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes ct: ciphertext + :param bytes iv: blocksize-sized initialization vector + """ + if not ct or len(ct) % bs != 0: + raise ValueError("Ciphertext is not blocksize aligned") + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + pt = [] + for i in xrange(0, len(ct), bs): + blk = ct[i:i + bs] + pt.append(strxor(r[0], decrypter(blk))) + r = r[1:] + [blk] + return b"".join(pt) + + +def cfb_encrypt(encrypter, bs, pt, iv): + """CFB encryption mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes pt: plaintext + :param bytes iv: blocksize-sized initialization vector + """ + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + ct = [] + for i in xrange(0, len(pt) + pad_size(len(pt), bs), bs): + ct.append(strxor(encrypter(r[0]), pt[i:i + bs])) + r = r[1:] + [ct[-1]] + return b"".join(ct) + + +def cfb_decrypt(encrypter, bs, ct, iv): + """CFB decryption mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes ct: ciphertext + :param bytes iv: blocksize-sized initialization vector + """ + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + pt = [] + for i in xrange(0, len(ct) + pad_size(len(ct), bs), bs): + blk = ct[i:i + bs] + pt.append(strxor(encrypter(r[0]), blk)) + r = r[1:] + [blk] + return b"".join(pt) + + +def _mac_shift(bs, data, xor_lsb=0): + num = (bytes2long(data) << 1) ^ xor_lsb + return long2bytes(num, bs)[-bs:] + + +Rb64 = 0b11011 +Rb128 = 0b10000111 + + +def _mac_ks(encrypter, bs): + Rb = Rb128 if bs == 16 else Rb64 + _l = encrypter(bs * b"\x00") + k1 = _mac_shift(bs, _l, Rb) if bytearray(_l)[0] & 0x80 > 0 else _mac_shift(bs, _l) + k2 = _mac_shift(bs, k1, Rb) if bytearray(k1)[0] & 0x80 > 0 else _mac_shift(bs, k1) + return k1, k2 + + +def mac(encrypter, bs, data): + """MAC (known here as CMAC, OMAC1) mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes data: data to authenticate + + Implementation is based on PyCrypto's CMAC one, that is in public domain. + """ + k1, k2 = _mac_ks(encrypter, bs) + if len(data) % bs == 0: + tail_offset = len(data) - bs + else: + tail_offset = len(data) - (len(data) % bs) + prev = bs * b"\x00" + for i in xrange(0, tail_offset, bs): + prev = encrypter(strxor(data[i:i + bs], prev)) + tail = data[tail_offset:] + return encrypter(strxor( + strxor(pad3(tail, bs), prev), + k1 if len(tail) == bs else k2, + )) + + +def acpkm_master(algo_class, encrypter, key_section_size, bs, keymat_len): + """ACPKM-Master key derivation + + :param algo_class: pygost.gost3412's algorithm class + :param encrypter: encrypting function, that takes block as an input + :param int key_section_size: ACPKM'es key section size (T*), in bytes + :param int bs: cipher's blocksize, bytes + :param int keymat_len: length of key material to produce + """ + return ctr_acpkm( + algo_class, + encrypter, + key_section_size, + bs, + data=b"\x00" * keymat_len, + iv=b"\xFF" * (bs // 2), + ) + + +def mac_acpkm_master(algo_class, encrypter, key_section_size, section_size, bs, data): + """OMAC-ACPKM-Master + + :param algo_class: pygost.gost3412's algorithm class + :param encrypter: encrypting function, that takes block as an input + :param int key_section_size: ACPKM'es key section size (T*), in bytes + :param int section_size: ACPKM'es section size (N), in bytes + :param int bs: cipher's blocksize, bytes + :param bytes data: data to authenticate + """ + if len(data) % bs == 0: + tail_offset = len(data) - bs + else: + tail_offset = len(data) - (len(data) % bs) + prev = bs * b"\x00" + sections = len(data) // section_size + if len(data) % section_size != 0: + sections += 1 + keymats = acpkm_master( + algo_class, + encrypter, + key_section_size, + bs, + (KEYSIZE + bs) * sections, + ) + for i in xrange(0, tail_offset, bs): + if i % section_size == 0: + keymat, keymats = keymats[:KEYSIZE + bs], keymats[KEYSIZE + bs:] + key, k1 = keymat[:KEYSIZE], keymat[KEYSIZE:] + encrypter = algo_class(key).encrypt + prev = encrypter(strxor(data[i:i + bs], prev)) + tail = data[tail_offset:] + if len(tail) == bs: + key, k1 = keymats[:KEYSIZE], keymats[KEYSIZE:] + encrypter = algo_class(key).encrypt + k2 = long2bytes(bytes2long(k1) << 1, size=bs) + if bytearray(k1)[0] & 0x80 != 0: + k2 = strxor(k2, long2bytes(Rb128 if bs == 16 else Rb64, size=bs)) + return encrypter(strxor( + strxor(pad3(tail, bs), prev), + k1 if len(tail) == bs else k2, + )) + + +def pad_iso10126(data, blocksize): + """ISO 10126 padding + + Does not exist in 34.13, but added for convenience. + It uses urandom call for getting the randomness. + """ + pad_len = blocksize - len(data) % blocksize + if pad_len == 0: + pad_len = blocksize + return b"".join((data, urandom(pad_len - 1), bytes((pad_len,)))) + + +def unpad_iso10126(data, blocksize): + """Unpad :py:func:`pygost.gost3413.pad_iso10126` + """ + if len(data) % blocksize != 0: + raise ValueError("Data length is not multiple of blocksize") + pad_len = bytearray(data)[-1] + if pad_len > blocksize: + raise ValueError("Padding length is bigger than blocksize") + return data[:-pad_len] diff --git a/pygost-5.13/build/lib/pygost/iface.py b/pygost-5.13/build/lib/pygost/iface.py new file mode 100644 index 0000000..e2d6a4c --- /dev/null +++ b/pygost-5.13/build/lib/pygost/iface.py @@ -0,0 +1,50 @@ +from abc import ABCMeta +from abc import abstractmethod + +from pygost.utils import hexenc + + +# This function is taken from six package as is +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get("__slots__") + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop("__dict__", None) + orig_vars.pop("__weakref__", None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +@add_metaclass(ABCMeta) +class PEP247(object): + @property + @abstractmethod + def digest_size(self): + """The size of the digest produced by the hashing objects. + """ + + @abstractmethod + def copy(self): + """Return a separate copy of this hashing object. + """ + + @abstractmethod + def update(self, data): + """Hash data into the current state of the hashing object. + """ + + @abstractmethod + def digest(self): + """Return the hash value as a string containing 8-bit data. + """ + + def hexdigest(self): + """Return the hash value as a string containing hexadecimal digits. + """ + return hexenc(self.digest()) diff --git a/pygost-5.13/build/lib/pygost/kdf.py b/pygost-5.13/build/lib/pygost/kdf.py new file mode 100644 index 0000000..4e404c6 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/kdf.py @@ -0,0 +1,81 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Key derivation functions, Р 50.1.113-2016, Р 1323565.1.020-2018 +""" + +import hmac + +from pygost.gost3410_vko import kek_34102012256 +from pygost.gost3410_vko import kek_34102012512 +from pygost.gost34112012256 import GOST34112012256 +from pygost.utils import bytes2long +from pygost.utils import long2bytes + + +def kdf_gostr3411_2012_256(key, label, seed): + """KDF_GOSTR3411_2012_256 + + :param bytes key: initial key + :param bytes label: label + :param bytes seed: seed + :returns: 32 bytes + """ + return hmac.new( + key=key, + msg=b"".join((b"\x01", label, b"\x00", seed, b"\x01\x00")), + digestmod=GOST34112012256, + ).digest() + + +def kdf_tree_gostr3411_2012_256(key, label, seed, keys, i_len=1): + """KDF_TREE_GOSTR3411_2012_256 + + :param bytes key: initial key + :param bytes label: label + :param bytes seed: seed + :param int keys: number of generated keys + :param int i_len: length of iterations value (called "R") + :returns: list of 256-bit keys + """ + keymat = [] + _len = long2bytes(keys * 32 * 8, size=1) + for i in range(keys): + keymat.append(hmac.new( + key=key, + msg=b"".join((long2bytes(i + 1, size=i_len), label, b"\x00", seed, _len)), + digestmod=GOST34112012256, + ).digest()) + return keymat + + +def keg(curve, prv, pub, h): + """Export key generation (Р 1323565.1.020-2018) + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param pub: public key + :type pub: (long, long) + :param bytes h: "h"-value, 32 bytes + """ + if len(h) != 32: + raise ValueError("h must be 32 bytes long") + ukm = bytes2long(h[:16]) + if ukm == 0: + ukm = 1 + if curve.point_size == 64: + return kek_34102012512(curve, prv, pub, ukm) + k_exp = kek_34102012256(curve, prv, pub, ukm) + return b"".join(kdf_tree_gostr3411_2012_256(k_exp, b"kdf tree", h[16:24], 2)) diff --git a/pygost-5.13/build/lib/pygost/mgm.py b/pygost-5.13/build/lib/pygost/mgm.py new file mode 100644 index 0000000..fb51343 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/mgm.py @@ -0,0 +1,168 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Multilinear Galois Mode (MGM) block cipher mode. +""" + +from hmac import compare_digest + +from pygost.gost3413 import pad1 +from pygost.utils import bytes2long +from pygost.utils import long2bytes +from pygost.utils import strxor + + +def _incr(data, bs): + return long2bytes(bytes2long(data) + 1, size=bs // 2) + + +def incr_r(data, bs): + return data[:bs // 2] + _incr(data[bs // 2:], bs) + + +def incr_l(data, bs): + return _incr(data[:bs // 2], bs) + data[bs // 2:] + + +def nonce_prepare(nonce): + """Prepare nonce for MGM usage + + It just clears MSB. + """ + n = bytearray(nonce) + n[0] &= 0x7F + return bytes(n) + + +class MGM(object): + # Implementation is fully based on go.cypherpunks.ru/gogost/mgm + def __init__(self, encrypter, bs, tag_size=None): + """Multilinear Galois Mode (MGM) block cipher mode + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize + :param int tag_size: authentication tag size + (defaults to blocksize if not specified) + """ + if bs not in (8, 16): + raise ValueError("Only 64/128-bit blocksizes allowed") + self.tag_size = bs if tag_size is None else tag_size + if self.tag_size < 4 or self.tag_size > bs: + raise ValueError("Invalid tag_size") + self.encrypter = encrypter + self.bs = bs + self.max_size = (1 << (bs * 8 // 2)) - 1 + self.r = 0x1B if bs == 8 else 0x87 + + def _validate_nonce(self, nonce): + if len(nonce) != self.bs: + raise ValueError("nonce length must be equal to cipher's blocksize") + if bytearray(nonce)[0] & 0x80 > 0: + raise ValueError("nonce must not have higher bit set") + + def _validate_sizes(self, plaintext, additional_data): + if len(plaintext) == 0 and len(additional_data) == 0: + raise ValueError("At least one of plaintext or additional_data required") + if len(plaintext) + len(additional_data) > self.max_size: + raise ValueError("plaintext+additional_data are too big") + + def _mul(self, x, y): + x = bytes2long(x) + y = bytes2long(y) + z = 0 + max_bit = 1 << (self.bs * 8 - 1) + while y > 0: + if y & 1 == 1: + z ^= x + if x & max_bit > 0: + x = ((x ^ max_bit) << 1) ^ self.r + else: + x <<= 1 + y >>= 1 + return long2bytes(z, size=self.bs) + + def _crypt(self, icn, data): + icn[0] &= 0x7F + enc = self.encrypter(bytes(icn)) + res = [] + while len(data) > 0: + res.append(strxor(self.encrypter(enc), data)) + enc = incr_r(enc, self.bs) + data = data[self.bs:] + return b"".join(res) + + def _auth(self, icn, text, ad): + icn[0] |= 0x80 + enc = self.encrypter(bytes(icn)) + _sum = self.bs * b"\x00" + ad_len = len(ad) + text_len = len(text) + while len(ad) > 0: + _sum = strxor(_sum, self._mul( + self.encrypter(enc), + pad1(ad[:self.bs], self.bs), + )) + enc = incr_l(enc, self.bs) + ad = ad[self.bs:] + while len(text) > 0: + _sum = strxor(_sum, self._mul( + self.encrypter(enc), + pad1(text[:self.bs], self.bs), + )) + enc = incr_l(enc, self.bs) + text = text[self.bs:] + _sum = strxor(_sum, self._mul(self.encrypter(enc), ( + long2bytes(ad_len * 8, size=self.bs // 2) + + long2bytes(text_len * 8, size=self.bs // 2) + ))) + return self.encrypter(_sum)[:self.tag_size] + + def seal(self, nonce, plaintext, additional_data): + """Seal plaintext + + :param bytes nonce: blocksize-sized nonce. + Assure that it does not have MSB bit set + (:py:func:`pygost.mgm.nonce_prepare` helps) + :param bytes plaintext: plaintext to be encrypted and authenticated + :param bytes additional_data: additional data to be authenticated + """ + self._validate_nonce(nonce) + self._validate_sizes(plaintext, additional_data) + icn = bytearray(nonce) + ciphertext = self._crypt(icn, plaintext) + tag = self._auth(icn, ciphertext, additional_data) + return ciphertext + tag + + def open(self, nonce, ciphertext, additional_data): + """Open ciphertext + + :param bytes nonce: blocksize-sized nonce. + Assure that it does not have MSB bit set + (:py:func:`pygost.mgm.nonce_prepare` helps) + :param bytes ciphertext: ciphertext to be decrypted and authenticated + :param bytes additional_data: additional data to be authenticated + :raises ValueError: if ciphertext authentication fails + """ + self._validate_nonce(nonce) + self._validate_sizes(ciphertext, additional_data) + icn = bytearray(nonce) + ciphertext, tag_expected = ( + ciphertext[:-self.tag_size], + ciphertext[-self.tag_size:], + ) + tag = self._auth(icn, ciphertext, additional_data) + if not compare_digest(tag_expected, tag): + raise ValueError("Invalid authentication tag") + return self._crypt(icn, ciphertext) diff --git a/pygost-5.13/build/lib/pygost/pbkdf2.py b/pygost-5.13/build/lib/pygost/pbkdf2.py new file mode 100644 index 0000000..0fd6ddc --- /dev/null +++ b/pygost-5.13/build/lib/pygost/pbkdf2.py @@ -0,0 +1,41 @@ +# coding: utf-8 +"""PBKDF2 implementation suitable for GOST R 34.11-94/34.11-2012. + +This implementation is based on Python 3.5.2 source code's one. +PyGOST does not register itself in hashlib anyway, so use it instead. +""" + + +from pygost.utils import bytes2long +from pygost.utils import long2bytes +from pygost.utils import strxor +from pygost.utils import xrange + + +def pbkdf2(hasher, password, salt, iterations, dklen): + """PBKDF2 implementation suitable for GOST R 34.11-94/34.11-2012 + """ + inner = hasher() + outer = hasher() + password = password + b"\x00" * (inner.block_size - len(password)) + inner.update(strxor(password, len(password) * b"\x36")) + outer.update(strxor(password, len(password) * b"\x5C")) + + def prf(msg): + icpy = inner.copy() + ocpy = outer.copy() + icpy.update(msg) + ocpy.update(icpy.digest()) + return ocpy.digest() + + dkey = b"" + loop = 1 + while len(dkey) < dklen: + prev = prf(salt + long2bytes(loop, 4)) + rkey = bytes2long(prev) + for _ in xrange(iterations - 1): + prev = prf(prev) + rkey ^= bytes2long(prev) + loop += 1 + dkey += long2bytes(rkey, inner.digest_size) + return dkey[:dklen] diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/__init__.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/__init__.pyi new file mode 100644 index 0000000..e69de29 diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost28147.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost28147.pyi new file mode 100644 index 0000000..be31261 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost28147.pyi @@ -0,0 +1,101 @@ +from typing import Callable +from typing import Dict +from typing import Sequence +from typing import Tuple + + +SBOXES = ... # type: Dict[str, Tuple[Tuple[int, ...], ...]] +BLOCKSIZE = ... # type: int + +Words = Tuple[int, int] + + +def block2ns(data: bytes) -> Words: ... + + +def ns2block(ns: Words) -> bytes: ... + + +def validate_key(key: bytes) -> None: ... + + +def validate_iv(iv: bytes) -> None: ... + + +def validate_sbox(sbox: str) -> None: ... + + +def xcrypt(seq: Sequence[int], sbox: str, key: bytes, ns: Words) -> Words: ... + + +def encrypt(sbox: str, key: bytes, ns: Words) -> Words: ... + + +def decrypt(sbox: str, key: bytes, ns: Words) -> Words: ... + + +def ecb( + key: bytes, + data: bytes, + action: Callable[[str, bytes, Words], Words], + sbox: str = ..., +) -> bytes: ... + + +def ecb_encrypt( + key: bytes, + data: bytes, + sbox: str = ..., +) -> bytes: ... + + +def ecb_decrypt( + key: bytes, + data: bytes, + sbox: str = ..., +) -> bytes: ... + + +def cbc_encrypt( + key: bytes, + data: bytes, + iv: bytes = ..., + pad: bool = ..., + sbox: str = ..., + mesh: bool = ..., +) -> bytes: ... + + +def cbc_decrypt( + key: bytes, + data: bytes, + pad: bool = ..., + sbox: str = ..., + mesh: bool = ..., +) -> bytes: ... + + +def cnt( + key: bytes, + data: bytes, + iv: bytes = ..., + sbox: str = ..., +) -> bytes: ... + + +def cfb_encrypt( + key: bytes, + data: bytes, + iv: bytes = ..., + sbox: str = ..., + mesh: bool = ..., +) -> bytes: ... + + +def cfb_decrypt( + key: bytes, + data: bytes, + iv: bytes = ..., + sbox: str = ..., + mesh: bool = ..., +) -> bytes: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost28147_mac.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost28147_mac.pyi new file mode 100644 index 0000000..70d90d6 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost28147_mac.pyi @@ -0,0 +1,25 @@ +from pygost.iface import PEP247 + + +class MAC(PEP247): + def __init__( + self, + key: bytes, + data: bytes = ..., + iv: bytes = ..., + sbox: str = ..., + ) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "MAC": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... + + +def new(key: bytes, data: bytes = ..., iv: bytes = ..., sbox: str = ...) -> MAC: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost3410.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost3410.pyi new file mode 100644 index 0000000..8f0dcb8 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost3410.pyi @@ -0,0 +1,72 @@ +from typing import Dict +from typing import Tuple + + +DEFAULT_CURVE = ... # type: GOST3410Curve +CURVES = ... # type: Dict[str, GOST3410Curve] +PublicKey = Tuple[int, int] + + +class GOST3410Curve(object): + p = ... # type: int + q = ... # type: int + a = ... # type: int + b = ... # type: int + x = ... # type: int + y = ... # type: int + cofactor = ... # type: int + e = ... # type: int + d = ... # type: int + name = ... # type: str + + def __init__( + self, + p: int, + q: int, + a: int, + b: int, + x: int, + y: int, + cofactor: int = 1, + e: int = None, + d: int = None, + name: str = None, + ) -> None: ... + + def pos(self, v: int) -> int: ... + + def exp(self, degree: int, x: int = ..., y: int = ...) -> int: ... + + def st(self) -> Tuple[int, int]: ... + + @property + def point_size(self) -> int: ... + + def contains(self, point: Tuple[int, int]) -> bool: ... + + +def public_key(curve: GOST3410Curve, prv: int) -> PublicKey: ... + + +def sign(curve: GOST3410Curve, prv: int, digest: bytes, rand: bytes = None) -> bytes: ... + + +def verify(curve: GOST3410Curve, pub: PublicKey, digest: bytes, signature: bytes) -> bool: ... + + +def prv_unmarshal(prv: bytes) -> int: ... + + +def prv_marshal(curve: GOST3410Curve, prv: int) -> bytes: ... + + +def pub_marshal(pub: PublicKey) -> bytes: ... + + +def pub_unmarshal(pub: bytes) -> PublicKey: ... + + +def uv2xy(curve: GOST3410Curve, u: int, v: int) -> Tuple[int, int]: ... + + +def xy2uv(curve: GOST3410Curve, x: int, y: int) -> Tuple[int, int]: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost3410_vko.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost3410_vko.pyi new file mode 100644 index 0000000..6ea9b82 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost3410_vko.pyi @@ -0,0 +1,17 @@ +from pygost.gost3410 import GOST3410Curve +from pygost.gost3410 import PublicKey + + +def ukm_unmarshal(ukm: bytes) -> int: ... + + +def kek(curve: GOST3410Curve, prv: int, pub: PublicKey, ukm: int) -> bytes: ... + + +def kek_34102001(curve: GOST3410Curve, prv: int, pub: PublicKey, ukm: int) -> bytes: ... + + +def kek_34102012256(curve: GOST3410Curve, prv: int, pub: PublicKey, ukm: int = ...) -> bytes: ... + + +def kek_34102012512(curve: GOST3410Curve, prv: int, pub: PublicKey, ukm: int = ...) -> bytes: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost34112012.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost34112012.pyi new file mode 100644 index 0000000..3d5cc41 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost34112012.pyi @@ -0,0 +1,18 @@ +from pygost.iface import PEP247 + + +class GOST34112012(PEP247): + block_size = ... # type: int + + def __init__(self, data: bytes = ..., digest_size: int = ...) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "GOST34112012": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost34112012256.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost34112012256.pyi new file mode 100644 index 0000000..a1d2a01 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost34112012256.pyi @@ -0,0 +1,21 @@ +from pygost.iface import PEP247 + + +class GOST34112012256(PEP247): + block_size = ... # type: int + + def __init__(self, data: bytes = ...) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "GOST34112012256": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... + + +def new(data: bytes = ...) -> GOST34112012256: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost34112012512.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost34112012512.pyi new file mode 100644 index 0000000..349bddd --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost34112012512.pyi @@ -0,0 +1,24 @@ +from pygost.iface import PEP247 + + +class GOST34112012512(PEP247): + block_size = ... # type: int + + def __init__(self, data: bytes = ...) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "GOST34112012512": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... + + +def new(data: bytes = ...) -> GOST34112012512: ... + + +def pbkdf2(password: bytes, salt: bytes, iterations: int, dklen: int) -> bytes: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost341194.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost341194.pyi new file mode 100644 index 0000000..24de2e4 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost341194.pyi @@ -0,0 +1,25 @@ +from pygost.iface import PEP247 + + +class GOST341194(PEP247): + sbox = ... # type: str + block_size = ... # type: int + + def __init__(self, data: bytes = ..., sbox: str = ...) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "GOST341194": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... + + +def new(data: bytes = ..., sbox: str = ...) -> GOST341194: ... + + +def pbkdf2(password: bytes, salt: bytes, iterations: int, dklen: int) -> bytes: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost3412.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost3412.pyi new file mode 100644 index 0000000..ef278b7 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost3412.pyi @@ -0,0 +1,18 @@ +class GOST3412Kuznechik(object): + blocksize = ... # type: int + + def __init__(self, key: bytes) -> None: ... + + def encrypt(self, blk: bytes) -> bytes: ... + + def decrypt(self, blk: bytes) -> bytes: ... + + +class GOST3412Magma(object): + blocksize = ... # type: int + + def __init__(self, key: bytes) -> None: ... + + def encrypt(self, blk: bytes) -> bytes: ... + + def decrypt(self, blk: bytes) -> bytes: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/gost3413.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/gost3413.pyi new file mode 100644 index 0000000..4cfd694 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/gost3413.pyi @@ -0,0 +1,81 @@ +from typing import Callable + + +def pad_size(data_size: int, blocksize: int) -> int: ... + + +def pad1(data: bytes, blocksize: int) -> bytes: ... + + +def pad2(data: bytes, blocksize: int) -> bytes: ... + + +def unpad2(data: bytes, blocksize: int) -> bytes: ... + + +def pad3(data: bytes, blocksize: int) -> bytes: ... + + +def ecb_encrypt(encrypter: Callable[[bytes], bytes], bs: int, pt: bytes) -> bytes: ... + + +def ecb_decrypt(decrypter: Callable[[bytes], bytes], bs: int, ct: bytes) -> bytes: ... + + +def acpkm(encrypter: Callable[[bytes], bytes], bs: int) -> bytes: ... + + +def ctr(encrypter: Callable[[bytes], bytes], bs: int, data: bytes, iv: bytes) -> bytes: ... + + +def ctr_acpkm( + algo_class: object, + encrypter: Callable[[bytes], bytes], + section_size: int, + bs: int, + data: bytes, + iv: bytes, +) -> bytes: ... + + +def ofb(encrypter: Callable[[bytes], bytes], bs: int, data: bytes, iv: bytes) -> bytes: ... + + +def cbc_encrypt(encrypter: Callable[[bytes], bytes], bs: int, pt: bytes, iv: bytes) -> bytes: ... + + +def cbc_decrypt(decrypter: Callable[[bytes], bytes], bs: int, ct: bytes, iv: bytes) -> bytes: ... + + +def cfb_encrypt(encrypter: Callable[[bytes], bytes], bs: int, pt: bytes, iv: bytes) -> bytes: ... + + +def cfb_decrypt(encrypter: Callable[[bytes], bytes], bs: int, ct: bytes, iv: bytes) -> bytes: ... + + +def mac(encrypter: Callable[[bytes], bytes], bs: int, data: bytes) -> bytes: ... + + +def acpkm_master( + algo_class: object, + encrypter: Callable[[bytes], bytes], + key_section_size: int, + bs: int, + keymat_len: int, +) -> bytes: ... + + +def mac_acpkm_master( + algo_class: object, + encrypter: Callable[[bytes], bytes], + key_section_size: int, + section_size: int, + bs: int, + data: bytes, +) -> bytes: ... + + +def pad_iso10126(data: bytes, blocksize: int) -> bytes: ... + + +def unpad_iso10126(data: bytes, blocksize: int) -> bytes: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/iface.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/iface.pyi new file mode 100644 index 0000000..a5c2a85 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/iface.pyi @@ -0,0 +1,19 @@ +from abc import ABCMeta +from abc import abstractmethod + + +class PEP247(metaclass=ABCMeta): + @abstractmethod + @property + def digest_size(self) -> int: ... + + @abstractmethod + def copy(self) -> "PEP247": ... + + @abstractmethod + def update(self, data: bytes) -> None: ... + + @abstractmethod + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/kdf.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/kdf.pyi new file mode 100644 index 0000000..ccab8af --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/kdf.pyi @@ -0,0 +1,22 @@ +from typing import Sequence +from typing import Tuple + +from pygost.gost3410 import GOST3410Curve + + +PublicKey = Tuple[int, int] + + +def kdf_gostr3411_2012_256(key: bytes, label: bytes, seed: bytes) -> bytes: ... + + +def kdf_tree_gostr3411_2012_256( + key: bytes, + label: bytes, + seed: bytes, + keys: int, + i_len: int = 1, +) -> Sequence[bytes]: ... + + +def keg(curve: GOST3410Curve, prv: int, pub: PublicKey, h: bytes) -> bytes: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/mgm.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/mgm.pyi new file mode 100644 index 0000000..81906b7 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/mgm.pyi @@ -0,0 +1,17 @@ +from typing import Callable + + +def nonce_prepare(nonce: bytes) -> bytes: ... + + +class MGM(object): + def __init__( + self, + encrypter: Callable[[bytes], bytes], + bs: int, + tag_size: int = None, + ) -> None: ... + + def seal(self, nonce: bytes, plaintext: bytes, additional_data: bytes) -> bytes: ... + + def open(self, nonce: bytes, ciphertext: bytes, additional_data: bytes) -> bytes: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/utils.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/utils.pyi new file mode 100644 index 0000000..76460e5 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/utils.pyi @@ -0,0 +1,19 @@ +from typing import AnyStr + + +def strxor(a: bytes, b: bytes) -> bytes: ... + + +def hexdec(data: AnyStr) -> bytes: ... + + +def hexenc(data: bytes) -> str: ... + + +def bytes2long(raw: bytes) -> int: ... + + +def long2bytes(n: int, size: int = ...) -> bytes: ... + + +def modinvert(a: int, n: int) -> int: ... diff --git a/pygost-5.13/build/lib/pygost/stubs/pygost/wrap.pyi b/pygost-5.13/build/lib/pygost/stubs/pygost/wrap.pyi new file mode 100644 index 0000000..776a6e7 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/stubs/pygost/wrap.pyi @@ -0,0 +1,31 @@ +from typing import Callable + + +def wrap_gost(ukm: bytes, kek: bytes, cek: bytes, sbox: str = ...) -> bytes: ... + + +def unwrap_gost(kek: bytes, data: bytes, sbox: str = ...) -> bytes: ... + + +def wrap_cryptopro(ukm: bytes, kek: bytes, cek: bytes, sbox: str = ...) -> bytes: ... + + +def unwrap_cryptopro(kek: bytes, data: bytes, sbox: str = ...) -> bytes: ... + + +def kexp15( + encrypter_key: Callable[[bytes], bytes], + encrypter_mac: Callable[[bytes], bytes], + bs: int, + key: bytes, + iv: bytes, +) -> bytes: ... + + +def kimp15( + encrypter_key: Callable[[bytes], bytes], + encrypter_mac: Callable[[bytes], bytes], + bs: int, + kexp: bytes, + iv: bytes, +) -> bytes: ... diff --git a/pygost-5.13/build/lib/pygost/test_cms.py b/pygost-5.13/build/lib/pygost/test_cms.py new file mode 100644 index 0000000..7e5781a --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_cms.py @@ -0,0 +1,1078 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from base64 import b64decode +from unittest import skipIf +from unittest import TestCase + +from six import text_type + +from pygost.gost28147 import cfb_decrypt +from pygost.gost3410 import CURVES +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import pub_marshal +from pygost.gost3410 import pub_unmarshal +from pygost.gost3410 import public_key +from pygost.gost3410 import verify +from pygost.gost3410_vko import kek_34102012256 +from pygost.gost3410_vko import ukm_unmarshal +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3413 import ctr_acpkm +from pygost.gost3413 import KEYSIZE +from pygost.gost3413 import mac as omac +from pygost.kdf import kdf_tree_gostr3411_2012_256 +from pygost.kdf import keg +from pygost.utils import hexdec +from pygost.wrap import kimp15 +from pygost.wrap import unwrap_cryptopro +from pygost.wrap import unwrap_gost + +try: + from pyderasn import DecodePathDefBy + from pyderasn import OctetString + + from pygost.asn1schemas.cms import ContentInfo + from pygost.asn1schemas.cms import SignedAttributes + from pygost.asn1schemas.oids import id_cms_mac_attr + from pygost.asn1schemas.oids import id_envelopedData + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm_omac + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_wrap_kexp15 + from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm + from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm_omac + from pygost.asn1schemas.oids import id_gostr3412_2015_magma_wrap_kexp15 + from pygost.asn1schemas.oids import id_messageDigest + from pygost.asn1schemas.oids import id_tc26_agreement_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_agreement_gost3410_2012_512 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA + from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256 + from pygost.asn1schemas.oids import id_tc26_gost3411_2012_512 + from pygost.asn1schemas.x509 import Certificate + from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters +except ImportError: + pyderasn_exists = False +else: + pyderasn_exists = True + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestSigned(TestCase): + """SignedData test vectors from "Использование + алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 в + криптографических сообщениях формата CMS" (TK26CMS.pdf) + """ + + def process_cms( + self, + content_info_raw, + prv_key_raw, + curve_name, + hasher, + ): + content_info, tail = ContentInfo().decode(content_info_raw) + self.assertSequenceEqual(tail, b"") + self.assertIsNotNone(content_info["content"].defined) + _, signed_data = content_info["content"].defined + self.assertEqual(len(signed_data["signerInfos"]), 1) + curve = CURVES[curve_name] + self.assertTrue(verify( + curve, + public_key(curve, prv_unmarshal(prv_key_raw)), + hasher(bytes(signed_data["encapContentInfo"]["eContent"])).digest()[::-1], + bytes(signed_data["signerInfos"][0]["signature"]), + )) + + def test_256(self): + content_info_raw = b64decode(""" +MIIBBQYJKoZIhvcNAQcCoIH3MIH0AgEBMQ4wDAYIKoUDBwEBAgIFADAbBgkqhkiG +9w0BBwGgDgQMVGVzdCBtZXNzYWdlMYHBMIG+AgEBMFswVjEpMCcGCSqGSIb3DQEJ +ARYaR29zdFIzNDEwLTIwMTJAZXhhbXBsZS5jb20xKTAnBgNVBAMTIEdvc3RSMzQx +MC0yMDEyICgyNTYgYml0KSBleGFtcGxlAgEBMAwGCCqFAwcBAQICBQAwDAYIKoUD +BwEBAQEFAARAkptb2ekZbC94FaGDQeP70ExvTkXtOY9zgz3cCco/hxPhXUVo3eCx +VNwDQ8enFItJZ8DEX4blZ8QtziNCMl5HbA== + """) + prv_key_raw = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1] + self.process_cms( + content_info_raw, + prv_key_raw, + "id-GostR3410-2001-CryptoPro-XchA-ParamSet", + GOST34112012256, + ) + + def test_512(self): + content_info_raw = b64decode(""" +MIIBSQYJKoZIhvcNAQcCoIIBOjCCATYCAQExDjAMBggqhQMHAQECAwUAMBsGCSqG +SIb3DQEHAaAOBAxUZXN0IG1lc3NhZ2UxggECMIH/AgEBMFswVjEpMCcGCSqGSIb3 +DQEJARYaR29zdFIzNDEwLTIwMTJAZXhhbXBsZS5jb20xKTAnBgNVBAMTIEdvc3RS +MzQxMC0yMDEyICg1MTIgYml0KSBleGFtcGxlAgEBMAwGCCqFAwcBAQIDBQAwDAYI +KoUDBwEBAQIFAASBgFyVohNhMHUi/+RAF3Gh/cC7why6v+4jPWVlx1TYlXtV8Hje +hI2Y+rP52/LO6EUHG/XcwCBbUxmRWsbUSRRBAexmaafkSdvv2FFwC8kHOcti+UPX +PS+KRYxT8vhcsBLWWxDkc1McI7aF09hqtED36mQOfACzeJjEoUjALpmJob1V + """) + prv_key_raw = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1] + self.process_cms( + content_info_raw, + prv_key_raw, + "id-tc26-gost-3410-12-512-paramSetB", + GOST34112012512, + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestDigested(TestCase): + """DigestedData test vectors from "Использование + алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 в + криптографических сообщениях формата CMS" (TK26CMS.pdf) + """ + + def process_cms(self, content_info_raw, hasher): + content_info, tail = ContentInfo().decode(content_info_raw) + self.assertSequenceEqual(tail, b"") + self.assertIsNotNone(content_info["content"].defined) + _, digested_data = content_info["content"].defined + self.assertSequenceEqual( + hasher(bytes(digested_data["encapContentInfo"]["eContent"])).digest(), + bytes(digested_data["digest"]), + ) + + def test_256(self): + content_info_raw = b64decode(""" +MIGdBgkqhkiG9w0BBwWggY8wgYwCAQAwDAYIKoUDBwEBAgIFADBXBgkqhkiG9w0B +BwGgSgRI0eUg4uXy8OgsINHy8Ojh7uboIOLt8/boLCDi5f7y+iDxIOzu8P8g8fLw +5evg7Ogg7eAg9fDg4fD7/yDv6/rq+yDI4+7w5eL7BCCd0v5OkECeXah/U5dtdAWw +wMrGKPxmmnQdUAY8VX6PUA== + """) + self.process_cms(content_info_raw, GOST34112012256) + + def test_512(self): + content_info_raw = b64decode(""" +MIG0BgkqhkiG9w0BBwWggaYwgaMCAQAwDAYIKoUDBwEBAgMFADBOBgkqhkiG9w0B +BwGgQQQ/MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAx +MjM0NTY3ODkwMTIzNDU2Nzg5MDEyBEAbVNAaSvW51cw9htaNKFRisZq8JHUiLzXA +hRIr5Lof+gCtMPh2ezqCOExldPAkwxHipIEzKwjvf0F5eJHBZG9I + """) + self.process_cms(content_info_raw, GOST34112012512) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestEnvelopedKTRI(TestCase): + """EnvelopedData KeyTransRecipientInfo-based test vectors from + "Использование алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 + в криптографических сообщениях формата CMS" (TK26CMS.pdf) + """ + + def process_cms( + self, + content_info_raw, + prv_key_our, + curve_name, + keker, + plaintext_expected, + ): + sbox = "id-tc26-gost-28147-param-Z" + content_info, tail = ContentInfo().decode(content_info_raw, ctx={ + "defines_by_path": [ + ( + ( + "content", + DecodePathDefBy(id_envelopedData), + "recipientInfos", + any, + "ktri", + "encryptedKey", + DecodePathDefBy(spki_algorithm), + "transportParameters", + "ephemeralPublicKey", + "algorithm", + "algorithm", + ), + ( + ( + ("..", "subjectPublicKey"), + { + id_tc26_gost3410_2012_256: OctetString(), + id_tc26_gost3410_2012_512: OctetString(), + }, + ), + ), + ) for spki_algorithm in ( + id_tc26_gost3410_2012_256, + id_tc26_gost3410_2012_512, + ) + ], + }) + self.assertSequenceEqual(tail, b"") + self.assertIsNotNone(content_info["content"].defined) + _, enveloped_data = content_info["content"].defined + eci = enveloped_data["encryptedContentInfo"] + ri = enveloped_data["recipientInfos"][0] + self.assertIsNotNone(ri["ktri"]["encryptedKey"].defined) + _, encrypted_key = ri["ktri"]["encryptedKey"].defined + ukm = bytes(encrypted_key["transportParameters"]["ukm"]) + spk = encrypted_key["transportParameters"]["ephemeralPublicKey"]["subjectPublicKey"] + self.assertIsNotNone(spk.defined) + _, pub_key_their = spk.defined + curve = CURVES[curve_name] + kek = keker(curve, prv_key_our, bytes(pub_key_their), ukm) + key_wrapped = bytes(encrypted_key["sessionEncryptedKey"]["encryptedKey"]) + mac = bytes(encrypted_key["sessionEncryptedKey"]["macKey"]) + cek = unwrap_cryptopro(kek, ukm + key_wrapped + mac, sbox=sbox) + ciphertext = bytes(eci["encryptedContent"]) + self.assertIsNotNone(eci["contentEncryptionAlgorithm"]["parameters"].defined) + _, encryption_params = eci["contentEncryptionAlgorithm"]["parameters"].defined + iv = bytes(encryption_params["iv"]) + self.assertSequenceEqual( + cfb_decrypt(cek, ciphertext, iv, sbox=sbox, mesh=True), + plaintext_expected, + ) + + def test_256(self): + content_info_raw = b64decode(""" +MIIKGgYJKoZIhvcNAQcDoIIKCzCCCgcCAQAxggE0MIIBMAIBADBbMFYxKTAnBgkq +hkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBH +b3N0UjM0MTAtMjAxMiAyNTYgYml0cyBleGNoYW5nZQIBATAfBggqhQMHAQEBATAT +BgcqhQMCAiQABggqhQMHAQECAgSBrDCBqTAoBCCVJxUMdbKRzCJ5K1NWJIXnN7Ul +zaceeFlblA2qH4wZrgQEsHnIG6B9BgkqhQMHAQIFAQGgZjAfBggqhQMHAQEBATAT +BgcqhQMCAiQABggqhQMHAQECAgNDAARAFoqoLg1lV780co6GdwtjLtS4KCXv9VGR +sd7PTPHCT/5iGbvOlKNW2I8UhayJ0dv7RV7Nb1lDIxPxf4Mbp2CikgQI1b4+WpGE +sfQwggjIBgkqhkiG9w0BBwEwHwYGKoUDAgIVMBUECHYNkdvFoYdyBgkqhQMHAQIF +AQGAggiYvFFpJKILAFdXjcdLLYv4eruXzL/wOXL8y9HHIDMbSzV1GM033J5Yt/p4 +H6JYe1L1hjAfE/BAAYBndof2sSUxC3/I7xj+b7M8BZ3GYPqATPtR4aCQDK6z91lx +nDBAWx0HdsStT5TOj/plMs4zJDadvIJLfjmGkt0Np8FSnSdDPOcJAO/jcwiOPopg ++Z8eIuZNmY4seegTLue+7DGqvqi1GdZdMnvXBFIKc9m5DUsC7LdyboqKImh6giZE +YZnxb8a2naersPylhrf+zp4Piwwv808yOrD6LliXUiH0RojlmuaQP4wBkb7m073h +MeAWEWSvyXzOvOOuFST/hxPEupiTRoHPUdfboJT3tNpizUhE384SrvXHpwpgivQ4 +J0zF2/uzTBEupXR6dFC9rTHAK3X79SltqBNnHyIXBwe+BMqTmKTfnlPVHBUfTXZg +oakDItwKwa1MBOZeciwtUFza+7o9FZhKIandb848chGdgd5O9ksaXvPJDIPxQjZd +EBVhnXLlje4TScImwTdvYB8GsI8ljKb2bL3FjwQWGbPaOjXc2D9w+Ore8bk1E4TA +ayhypU7MH3Mq1EBZ4j0iROEFBQmYRZn8vAKZ0K7aPxcDeAnKAJxdokqrMkLgI6WX +0glh/3Cs9dI+0D2GqMSygauKCD0vTIo3atkEQswDZR4pMx88gB4gmx7iIGrc/ZXs +ZqHI7NQqeKtBwv2MCIj+/UTqdYDqbaniDwdVS8PE9nQnNU4gKffq3JbT+wRjJv6M +Dr231bQHgAsFTVKbZgoL4gj4V7bLQUmW06+W1BQUJ2+Sn7fp+Xet9Xd3cGtNdxzQ +zl6sGuiOlTNe0bfKP7QIMC7ekjflLBx8nwa2GZG19k3O0Z9JcDdN/kz6bGpPNssY +AIOkTvLQjxIM9MhRqIv6ee0rowTWQPwXJP7yHApop4XZvVX6h9gG2gazqbDej2lo +tAcfRAKj/LJ/bk9+OlNXOXVCKnwE1kXxZDsNJ51GdCungC56U/hmd3C1RhSLTpEc +FlOWgXKNjbn6SQrlq1yASKKr80T0fL7PFoYwKZoQbKMAVZQC1VBWQltHkEzdL73x +FwgZULNfdflF8sEhFC/zsVqckD/UnhzJz88PtCslMArJ7ntbEF1GzsSSfRfjBqnl +kSUreE5XX6+c9yp5HcJBiMzp6ZqqWWaED5Y5xp1hZeYjuKbDMfY4tbWVc7Hy0dD2 +KGfZLp5umqvPNs7aVBPmvuxtrnxcJlUB8u2HoiHc6/TuhrpaopYGBhxL9+kezuLR +v18nsAg8HOmcCNUS46NXQj/Mdpx8W+RsyzCQkJjieT/Yed20Zxq1zJoXIS0xAaUH +TdE2dWqiT6TGlh/KQYk3KyFPNnDmzJm04a2VWIwpp4ypXyxrB7XxnVY6Q4YBYbZs +FycxGjJWqj7lwc+lgZ8YV2WJ4snEo2os8SsA2GFWcUMiVTHDnEJvphDHmhWsf26A +bbRqwaRXNjhj05DamTRsczgvfjdl1pk4lJYE4ES3nixtMe4s1X8nSmM4KvfyVDul +J8uTpw1ZFnolTdfEL63BSf4FREoEqKB7cKuD7cpn7Rg4kRdM0/BLZGuxkH+pGMsI +Bb8LecUWyjGsI6h74Wz/U2uBrfgdRqhR+UsfB2QLaRgM6kCXZ4vM0auuzBViFCwK +tYMHzZWWz8gyVtJ0mzt1DrHCMx4pTS4yOhv4RkXBS/rub4VhVIsOGOGar5ZYtH47 +uBbdw3NC05JIFM7lI31d0s1fvvkTUR7eaqRW+SnR2c2oHpWlSO+Q0mrzx+vvOTdj +xa713YtklBvyUUQr2SIbsXGpFnwjn+sXK1onAavp/tEax8sNZvxg5yeseFcWn+gD +4rjk9FiSd1wp4fTDQFJ19evqruqKlq6k18l/ZAyUcEbIWSz2s3HfAAoAQyFPX1Q2 +95gVhRRw6lP4S6VPCfn/f+5jV4TcT6W/giRaHIk9Hty+g8bx1bFXaKVkQZ5R2Vmk +qsZ65ZgCrYQJmcErPmYybvP7NBeDS4AOSgBQAGMQF4xywdNm6bniWWo3N/xkFv32 +/25x8okGgD8QcYKmhzieLSSzOvM/exB14RO84YZOkZzm01Jll0nac/LEazKoVWbn +0VdcQ7pYEOqeMBXipsicNVYA/uhonp6op9cpIVYafPr0npCGwwhwcRuOrgSaZyCn +VG2tPkEOv9LKmUbhnaDA2YUSzOOjcCpIVvTSBnUEiorYpfRYgQLrbcd2qhVvNCLX +8ujZfMqXQXK8n5BK8JxNtczvaf+/2dfv1dQl0lHEAQhbNcsJ0t5GPhsSCC5oMBJl +ZJuOEO/8PBWKEnMZOM+Dz7gEgsBhGyMFFrKpiwQRpyEshSD2QpnK6Lp0t5C8Za2G +lhyZsEr+93AYOb5mm5+z02B4Yq9+RpepvjoqVeq/2uywZNq9MS98zVgNsmpryvTZ +3HJHHB20u2jcVu0G3Nhiv22lD70JWCYFAOupjgVcUcaBxjxwUMAvgHg7JZqs6mC6 +tvTKwQ4NtDhoAhARlDeWSwCWb2vPH2H7Lmqokif1RfvJ0hrLzkJuHdWrzIYzXpPs ++v9XJxLvbdKi9KU1Halq9S8dXT1fvs9DJTpUV/KW7QkRsTQJhTJBkQ07WUSJ4gBS +Qp4efxSRNIfMj7DR6qLLf13RpIPTJO9/+gNuBIFcupWVfUL7tJZt8Qsf9eGwZfP+ +YyhjC8AyZjH4/9RzLHSjuq6apgw3Mzw0j572Xg6xDLMK8C3Tn/vrLOvAd96b9MkF +3+ZHSLW3IgOiy+1jvK/20CZxNWc+pey8v4zji1hI17iohsipX/uZKRxhxF6+Xn2R +UQp6qoxHAspNXgWQ57xg7C3+gmi4ciVr0fT9pg54ogcowrRH+I6wd0EpeWPbzfnQ +pRmMVN+YtRsrEHwH3ToQ/i4vrtgA+eONuKT2uKZFikxA+VNmeeGdhkgqETMihQ== + """) + prv_key_our = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1] + + def keker(curve, prv, pub, ukm): + return kek_34102012256( + curve, + prv_unmarshal(prv), + pub_unmarshal(pub), + ukm_unmarshal(ukm), + ) + + self.process_cms( + content_info_raw, + prv_key_our, + "id-GostR3410-2001-CryptoPro-XchA-ParamSet", + keker, + b"Test data to encrypt.\n" * 100, + ) + + def test_512(self): + content_info_raw = b64decode(""" +MIIB0gYJKoZIhvcNAQcDoIIBwzCCAb8CAQAxggF8MIIBeAIBADBbMFYxKTAnBgkq +hkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBH +b3N0UjM0MTAtMjAxMiA1MTIgYml0cyBleGNoYW5nZQIBATAhBggqhQMHAQEBAjAV +BgkqhQMHAQIBAgIGCCqFAwcBAQIDBIHyMIHvMCgEIIsYzbVLn33aLinQ7SLNA7y+ +Lrm02khqDCfXrNS9iiMhBATerS8zoIHCBgkqhQMHAQIFAQGggaowIQYIKoUDBwEB +AQIwFQYJKoUDBwECAQICBggqhQMHAQECAwOBhAAEgYAYiTVLKpSGaAvjJEDQ0hdK +qR/jek5Q9Q2pXC+NkOimQh7dpCi+wcaHlPcBk96hmpnOFvLaiokX8V6jqtBl5gdk +M40kOXv8kcDdTzEVKA/ZLxA8xanL+gTD6ZjaPsUu06nsA2MoMBWcHLUzueaP3bGT +/yHTV+Za5xdcQehag/lNBgQIvCw4uUl0XC4wOgYJKoZIhvcNAQcBMB8GBiqFAwIC +FTAVBAj+1QzaXaN9FwYJKoUDBwECBQEBgAyK54euw0sHhEVEkA0= + """) + prv_key_our = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1] + + def keker(curve, prv, pub, ukm): + return kek_34102012256( + curve, + prv_unmarshal(prv), + pub_unmarshal(pub), + ukm_unmarshal(ukm), + ) + + self.process_cms( + content_info_raw, + prv_key_our, + "id-tc26-gost-3410-12-512-paramSetB", + keker, + b"Test message", + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestEnvelopedKARI(TestCase): + """EnvelopedData KeyAgreeRecipientInfo-based test vectors from + "Использование алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 + в криптографических сообщениях формата CMS" (TK26CMS.pdf) + """ + + def process_cms( + self, + content_info_raw, + prv_key_our, + curve_name, + keker, + plaintext_expected, + ): + sbox = "id-tc26-gost-28147-param-Z" + content_info, tail = ContentInfo().decode(content_info_raw, ctx={ + "defines_by_path": [ + ( + ( + "content", + DecodePathDefBy(id_envelopedData), + "recipientInfos", + any, + "kari", + "originator", + "originatorKey", + "algorithm", + "algorithm", + ), + ( + ( + ("..", "publicKey"), + { + id_tc26_gost3410_2012_256: OctetString(), + id_tc26_gost3410_2012_512: OctetString(), + }, + ), + ), + ) for _ in ( + id_tc26_gost3410_2012_256, + id_tc26_gost3410_2012_512, + ) + ], + }) + self.assertSequenceEqual(tail, b"") + self.assertIsNotNone(content_info["content"].defined) + _, enveloped_data = content_info["content"].defined + eci = enveloped_data["encryptedContentInfo"] + kari = enveloped_data["recipientInfos"][0]["kari"] + self.assertIsNotNone(kari["originator"]["originatorKey"]["publicKey"].defined) + _, pub_key_their = kari["originator"]["originatorKey"]["publicKey"].defined + ukm = bytes(kari["ukm"]) + rek = kari["recipientEncryptedKeys"][0] + curve = CURVES[curve_name] + kek = keker(curve, prv_key_our, bytes(pub_key_their), ukm) + self.assertIsNotNone(rek["encryptedKey"].defined) + _, encrypted_key = rek["encryptedKey"].defined + key_wrapped = bytes(encrypted_key["encryptedKey"]) + mac = bytes(encrypted_key["macKey"]) + cek = unwrap_gost(kek, ukm + key_wrapped + mac, sbox=sbox) + ciphertext = bytes(eci["encryptedContent"]) + self.assertIsNotNone(eci["contentEncryptionAlgorithm"]["parameters"].defined) + _, encryption_params = eci["contentEncryptionAlgorithm"]["parameters"].defined + iv = bytes(encryption_params["iv"]) + self.assertSequenceEqual( + cfb_decrypt(cek, ciphertext, iv, sbox=sbox, mesh=True), + plaintext_expected, + ) + + def test_256(self): + content_info_raw = b64decode(""" +MIIBhgYJKoZIhvcNAQcDoIIBdzCCAXMCAQIxggEwoYIBLAIBA6BooWYwHwYIKoUD +BwEBAQEwEwYHKoUDAgIkAAYIKoUDBwEBAgIDQwAEQPAdWM4pO38iZ49UjaXQpq+a +jhTa4KwY4B9TFMK7AiYmbFKE0eX/wvu69kFMQ2o3OJTnMOlr1WHiPYOmNO6C5hOh +CgQIX+vNomZakEIwIgYIKoUDBwEBAQEwFgYHKoUDAgINADALBgkqhQMHAQIFAQEw +gYwwgYkwWzBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxl +LmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgMjU2IGJpdHMgZXhjaGFuZ2UC +AQEEKjAoBCCNhrZOr7x2fsjjQAeDMv/tSoNRQSSQzzxgqdnYxJ3fIAQEgYLqVDA6 +BgkqhkiG9w0BBwEwHwYGKoUDAgIVMBUECHVmR/S+hlYiBgkqhQMHAQIFAQGADEI9 +UNjyuY+54uVcHw== + """) + prv_key_our = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1] + + def keker(curve, prv, pub, ukm): + return kek_34102012256( + curve, + prv_unmarshal(prv), + pub_unmarshal(pub), + ukm_unmarshal(ukm), + ) + + self.process_cms( + content_info_raw, + prv_key_our, + "id-GostR3410-2001-CryptoPro-XchA-ParamSet", + keker, + b"Test message", + ) + + def test_512(self): + content_info_raw = b64decode(""" +MIIBzAYJKoZIhvcNAQcDoIIBvTCCAbkCAQIxggF2oYIBcgIBA6CBraGBqjAhBggq +hQMHAQEBAjAVBgkqhQMHAQIBAgIGCCqFAwcBAQIDA4GEAASBgCB0nQy/Ljva/mRj +w6o+eDKIvnxwYIQB5XCHhZhCpHNZiWcFxFpYXZLWRPKifOxV7NStvqGE1+fkfhBe +btkQu0tdC1XL3LO2Cp/jX16XhW/IP5rKV84qWr1Owy/6tnSsNRb+ez6IttwVvaVV +pA6ONFy9p9gawoC8nitvAVJkWW0PoQoECDVfxzxgMTAHMCIGCCqFAwcBAQECMBYG +ByqFAwICDQAwCwYJKoUDBwECBQEBMIGMMIGJMFswVjEpMCcGCSqGSIb3DQEJARYa +R29zdFIzNDEwLTIwMTJAZXhhbXBsZS5jb20xKTAnBgNVBAMTIEdvc3RSMzQxMC0y +MDEyIDUxMiBiaXRzIGV4Y2hhbmdlAgEBBCowKAQg8C/OcxRR0Uq8nDjHrQlayFb3 +WFUZEnEuAKcuG6dTOawEBLhi9hIwOgYJKoZIhvcNAQcBMB8GBiqFAwICFTAVBAiD +1wH+CX6CwgYJKoUDBwECBQEBgAzUvQI4H2zRfgNgdlY= + """) + prv_key_our = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1] + + def keker(curve, prv, pub, ukm): + return kek_34102012256( + curve, + prv_unmarshal(prv), + pub_unmarshal(pub), + ukm_unmarshal(ukm), + ) + + self.process_cms( + content_info_raw, + prv_key_our, + "id-tc26-gost-3410-12-512-paramSetB", + keker, + b"Test message", + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestR132356510252019(TestCase): + """Test vectors from Р 1323565.1.025-2019 + """ + def setUp(self): + self.curve256 = CURVES["id-tc26-gost-3410-2012-256-paramSetA"] + self.curve512 = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + self.psk = hexdec("8F5EEF8814D228FB2BBC5612323730CFA33DB7263CC2C0A01A6C6953F33D61D5")[::-1] + + self.ca_prv = prv_unmarshal(hexdec("092F8D059E97E22B90B1AE99F0087FC4D26620B91550CBB437C191005A290810")[::-1]) + self.ca_pub = public_key(self.curve256, self.ca_prv) + self.ca_cert = Certificate().decod(b64decode(""" +MIIB8DCCAZ2gAwIBAgIEAYy6gTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYD +VQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwaDAhBggqhQMHAQEB +ATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MABEAaSoKcjw54UACci6svELNF0IYM +RIW8urUsqamIpoG46XCqrVOuI6Q13N4dwcRsbZdqByf+GC2f5ZfO3baN5bTKo4GF +MIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJjsCecS2npzESoTowODENMAsGA1UE +ChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0 +ggQBjLqBMB0GA1UdDgQWBBSA2Qz3mfhmTZNTiY7AnnEtp6cxEjAKBggqhQMHAQED +AgNBAAgv248F4OeNCkhlzJWec0evHYnMBlSzk1lDm0F875B7CqMrKh2MtJHXenbj +Gc2uRn2IwgmSf/LZDrYsKKqZSxk= +""")) + + self.sender256_prv = prv_unmarshal(hexdec("0B20810E449978C7C3B76C6FF77A16C532421139344A058EF56310B6B6F377E8")[::-1]) + self.sender256_pub = public_key(self.curve256, self.sender256_prv) + self.sender256_cert = Certificate().decod(b64decode(""" +MIIB8zCCAaCgAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD +VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwaDAhBggqhQMH +AQEBATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MABECWKQ0TYllqg4GmY3tBJiyz +pXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ6VbT +o4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJjsCecS2npzESoTowODENMAsG +A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt +Yml0ggQBjLqBMB0GA1UdDgQWBBTRnChHSWbQYwnJC62n2zu5Njd03zAKBggqhQMH +AQEDAgNBAB41oijaXSEn58l78y2rhxY35/lKEq4XWZ70FtsNlVxWATyzgO5Wliwn +t1O4GoZsxx8r6T/i7VG65UNmQlwdOKQ= +""")) + + self.recipient256_prv = prv_unmarshal(hexdec("0DC8DC1FF2BC114BABC3F1CA8C51E4F58610427E197B1C2FBDBA4AE58CBFB7CE")[::-1]) + self.recipient256_pub = public_key(self.curve256, self.recipient256_prv) + self.recipient256_cert = Certificate().decod(b64decode(""" +MIIB8jCCAZ+gAwIBAgIEAYy6gzAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD +VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDBoMCEGCCqFAwcB +AQEBMBUGCSqFAwcBAgEBAQYIKoUDBwEBAgIDQwAEQL8nghlzLGMKWHuWhNMPMN5u +L6SkGqRiJ6qZxZb+4dPKbBT9LNVvNKtwUed+BeE5kfqOfolPgFusnL1rnO9yREOj +gYUwgYIwYQYDVR0BBFowWIAUgNkM95n4Zk2TU4mOwJ5xLaenMRKhOjA4MQ0wCwYD +VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1i +aXSCBAGMuoEwHQYDVR0OBBYEFLue+PUb9Oe+pziBU+MvNejjgrzFMAoGCCqFAwcB +AQMCA0EAPP9Oad1/5jwokSjPpccsQ0xCdVYM+mGQ0IbpiZxQj8gnkt8sq4jR6Ya+ +I/BDkbZNDNE27TU1p3t5rE9NMEeViA== +""")) + + self.sender512_prv = prv_unmarshal(hexdec("F95A5D44C5245F63F2E7DF8E782C1924EADCB8D06C52D91023179786154CBDB1561B4DF759D69F67EE1FBD5B68800E134BAA12818DA4F3AC75B0E5E6F9256911")[::-1]) + self.sender512_pub = public_key(self.curve512, self.sender512_prv) + self.sender512_cert = Certificate().decod(b64decode(""" +MIICNjCCAeOgAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD +VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQwgaowIQYIKoUD +BwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwOBhAAEgYC0i7davCkOGGVcYqFP +tS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDPs2Sqa13ZN+Ts +/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo6xGunecH1/G4 +hMts9HYLnxbwJDMNVGuIHV6gzqOBhTCBgjBhBgNVHQEEWjBYgBSA2Qz3mfhmTZNT +iY7AnnEtp6cxEqE6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6 +IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUK+l9HAscONGx +zCcRpxRAmFHvlXowCgYIKoUDBwEBAwIDQQAbjA0Q41/rIKOOvjHKsAsoEJM+WJf6 +/PKXg2JaStthmw99bdtwwkU/qDbcje2tF6mt+XWyQBXwvfeES1GFY9fJ +""")) + + self.recipient512_prv = prv_unmarshal(hexdec("A50315981F0A7C7FC05B4EB9591A62B1F84BD6FD518ACFCEDF0A7C9CF388D1F18757C056ADA5B38CBF24CDDB0F1519EF72DB1712CEF1920952E94AF1F9C575DC")[::-1]) + self.recipient512_pub = public_key(self.curve512, self.recipient512_prv) + self.recipient512_cert = Certificate().decod(b64decode(""" +MIICNTCCAeKgAwIBAgIEAYy6hTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD +VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgNTEyLWJpdDCBqjAhBggqhQMH +AQEBAjAVBgkqhQMHAQIBAgEGCCqFAwcBAQIDA4GEAASBgKauwGYvUkzz19g0LP/p +zeRdmwy1m+QSy9W5ZrL/AGuJofm2ARjz40ozNbW6bp9hkHu8x66LX7u5zz+QeS2+ +X5om18UXriComgO0+qhZbc+Hzu0eQ8FjOd8LpLk3TzzfBltfLOX5IiPLjeum+pSP +0QjoXAVcrop//B4yvZIukvROo4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJ +jsCecS2npzESoTowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjog +R09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBSrXT5VKhm/5uff +kwW0XpG19k6AajAKBggqhQMHAQEDAgNBAAJBpsHRrQKZGb22LOzaReEB8rl2MbIR +ja64NaM5h+cAFoHm6t/k+ziLh2A11rTakR+5of4NQ3EjEhuPtomP2tc= +""")) + + def test_certs(self): + """Certificates signatures + """ + for prv, pub, curve, cert in ( + (self.ca_prv, self.ca_pub, self.curve256, self.ca_cert), + (self.sender256_prv, self.sender256_pub, self.curve256, self.sender256_cert), + (self.recipient256_prv, self.recipient256_pub, self.curve256, self.recipient256_cert), + (self.sender512_prv, self.sender512_pub, self.curve512, self.sender512_cert), + (self.recipient512_prv, self.recipient512_pub, self.curve512, self.recipient512_cert), + ): + pub_our = public_key(curve, prv) + self.assertEqual(pub_our, pub) + self.assertSequenceEqual( + pub_marshal(pub_our), + bytes(OctetString().decod(bytes( + cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"] + ))), + ) + + for cert in ( + self.ca_cert, + self.sender256_cert, + self.recipient256_cert, + self.sender512_cert, + self.recipient512_cert, + ): + self.assertTrue(verify( + self.curve256, + self.ca_pub, + GOST34112012256(cert["tbsCertificate"].encode()).digest()[::-1], + bytes(cert["signatureValue"]), + )) + + def test_signed_with_attrs(self): + ci = ContentInfo().decod(b64decode(""" +MIIENwYJKoZIhvcNAQcCoIIEKDCCBCQCAQExDDAKBggqhQMHAQECAzA7BgkqhkiG +9w0BBwGgLgQsyu7t8vDu6/zt++kg7/Do7OXwIOTr/yDx8vDz6vLz8PsgU2lnbmVk +RGF0YS6gggI6MIICNjCCAeOgAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYD +VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1i +aXQwHhcNMDEwMTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRU +SzI2MSowKAYDVQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQw +gaowIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwOBhAAEgYC0i7da +vCkOGGVcYqFPtS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDP +s2Sqa13ZN+Ts/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo +6xGunecH1/G4hMts9HYLnxbwJDMNVGuIHV6gzqOBhTCBgjBhBgNVHQEEWjBYgBSA +2Qz3mfhmTZNTiY7AnnEtp6cxEqE6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMT +HkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQU +K+l9HAscONGxzCcRpxRAmFHvlXowCgYIKoUDBwEBAwIDQQAbjA0Q41/rIKOOvjHK +sAsoEJM+WJf6/PKXg2JaStthmw99bdtwwkU/qDbcje2tF6mt+XWyQBXwvfeES1GF +Y9fJMYIBlDCCAZACAQEwQDA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBU +SzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQCBAGMuoQwCgYIKoUDBwEBAgOgga0w +GAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwMzIw +MTk1NTIyWjAiBgkqhkiG9w0BCWIxFQQTU2lnbmVkIGF0dHIncyB2YWx1ZTBPBgkq +hkiG9w0BCQQxQgRAUdPHEukF5BIfo9DoQIMdnB0ZLkzq0RueEUZSNv07A7C+GKWi +G62fueArg8uPCHPTUN6d/42p33fgMkEwH7f7cDAKBggqhQMHAQEBAgSBgGUnVka8 +FvTlClmOtj/FUUacBdE/nEBeMLOO/535VDYrXlftPE6zQf/4ghS7TQG2VRGQ3GWD ++L3+W09A7d5uyyTEbvgtdllUG0OyqFwKmJEaYsMin87SFVs0cn1PGV1fOKeLluZa +bLx5whxd+mzlpekL5i6ImRX+TpERxrA/xSe5 +""")) + _, sd = ci["content"].defined + content = bytes(sd["encapContentInfo"]["eContent"]) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры SignedData."), + ) + si = sd["signerInfos"][0] + self.assertEqual( + si["digestAlgorithm"]["algorithm"], + id_tc26_gost3411_2012_512, + ) + digest = [ + bytes(attr["attrValues"][0].defined[1]) for attr in si["signedAttrs"] + if attr["attrType"] == id_messageDigest + ][0] + self.assertSequenceEqual(digest, GOST34112012512(content).digest()) + self.assertTrue(verify( + self.curve512, + self.sender512_pub, + GOST34112012512( + SignedAttributes(si["signedAttrs"]).encode() + ).digest()[::-1], + bytes(si["signature"]), + )) + + def test_signed_without_attrs(self): + ci = ContentInfo().decod(b64decode(""" +MIIDAQYJKoZIhvcNAQcCoIIC8jCCAu4CAQExDDAKBggqhQMHAQECAjA7BgkqhkiG +9w0BBwGgLgQsyu7t8vDu6/zt++kg7/Do7OXwIOTr/yDx8vDz6vLz8PsgU2lnbmVk +RGF0YS6gggH3MIIB8zCCAaCgAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYD +VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1i +aXQwHhcNMDEwMTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRU +SzI2MSowKAYDVQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQw +aDAhBggqhQMHAQEBATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MABECWKQ0TYllq +g4GmY3tBJiyzpXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZ +huJaJfqZ6VbTo4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJjsCecS2npzES +oTowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4x +MC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBTRnChHSWbQYwnJC62n2zu5Njd0 +3zAKBggqhQMHAQEDAgNBAB41oijaXSEn58l78y2rhxY35/lKEq4XWZ70FtsNlVxW +ATyzgO5Wliwnt1O4GoZsxx8r6T/i7VG65UNmQlwdOKQxgaIwgZ8CAQEwQDA4MQ0w +CwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1 +Ni1iaXQCBAGMuoIwCgYIKoUDBwEBAgIwCgYIKoUDBwEBAQEEQC6jZPA59szL9FiA +0wC71EBE42ap6gKxklT800cu2FvbLu972GJYNSI7+UeanVU37OVWyenEXi2E5HkU +94kBe8Q= +""")) + _, sd = ci["content"].defined + content = bytes(sd["encapContentInfo"]["eContent"]) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры SignedData."), + ) + si = sd["signerInfos"][0] + self.assertEqual( + si["digestAlgorithm"]["algorithm"], + id_tc26_gost3411_2012_256, + ) + self.assertTrue(verify( + self.curve256, + self.sender256_pub, + GOST34112012256(content).digest()[::-1], + bytes(si["signature"]), + )) + + def test_kari_ephemeral(self): + ci = ContentInfo().decod(b64decode(""" +MIIB/gYJKoZIhvcNAQcDoIIB7zCCAesCAQIxggFioYIBXgIBA6CBo6GBoDAXBggq +hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAe+itJVNbHM35RHfzuwFJPYdPXqtW +8hNEF7Z/XFEE2T71SRkhFX7ozYKQNh/TkVY9D4vG0LnD9Znr/pJyOjpsNb+dPcKX +Kbk/0JQxoPGHxFzASVAFq0ov/yBe2XGFWMeKUqtaAr7SvoYS0oEhT5EuT8BXmecd +nRe7NqOzESpb15ahIgQgsqHxOcdOp03l11S7k3OH1k1HNa5F8m9ctrOzH2846FMw +FwYJKoUDBwEBBwIBMAoGCCqFAwcBAQYCMHYwdDBAMDgxDTALBgNVBAoTBFRLMjYx +JzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdAIEAYy6hQQw +SxLc18zMwzLwXbcKqYhV/VzsdBgVArOHsSBIbaThJWE7zI37VGPMQJM5VXJ7GVcL +MF0GCSqGSIb3DQEHATAfBgkqhQMHAQEFAgIwEgQQ6EeVlADDCz2cdEWKy+tM94Av +yIFl/Ie4VeFFuczTsMsIaOUEe3Jn9GeVp8hZSj3O2q4hslQ/u/+Gj4QkSHm/M0ih +ITAfBgkqhQMHAQAGAQExEgQQs1t6D3J3WCEvxunnEE15NQ== +""")) + _, ed = ci["content"].defined + kari = ed["recipientInfos"][0]["kari"] + orig_key = kari["originator"]["originatorKey"] + self.assertEqual(orig_key["algorithm"]["algorithm"], id_tc26_gost3410_2012_512) + self.assertEqual( + GostR34102012PublicKeyParameters().decod( + bytes(orig_key["algorithm"]["parameters"]) + )["publicKeyParamSet"], + id_tc26_gost3410_2012_512_paramSetA, + ) + orig_pub = pub_unmarshal( + bytes(OctetString().decod(bytes(orig_key["publicKey"]))), + ) + ukm = bytes(kari["ukm"]) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_wrap_kexp15, + ) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_512, + ) + kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"]) + keymat = keg(self.curve512, self.recipient512_prv, orig_pub, ukm) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Kuznechik(kek).encrypt, + GOST3412Kuznechik(kim).encrypt, + GOST3412Kuznechik.blocksize, + kexp, + ukm[24:24 + GOST3412Kuznechik.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_ctracpkm_omac, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + self.assertEqual(ed["unprotectedAttrs"][0]["attrType"], id_cms_mac_attr) + encrypted_mac = bytes(ed["unprotectedAttrs"][0]["attrValues"][0].defined[1]) + encrypted_content = bytes(eci["encryptedContent"]) + cek_enc, cek_mac = kdf_tree_gostr3411_2012_256( + cek, b"kdf tree", eci_ukm[GOST3412Kuznechik.blocksize // 2:], 2, + ) + content_and_tag = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(cek_enc).encrypt, + 256 * 1024, + GOST3412Kuznechik.blocksize, + encrypted_content + encrypted_mac, + eci_ukm[:GOST3412Kuznechik.blocksize // 2], + ) + content = content_and_tag[:-GOST3412Kuznechik.blocksize] + tag_expected = content_and_tag[-GOST3412Kuznechik.blocksize:] + self.assertSequenceEqual( + omac( + GOST3412Kuznechik(cek_mac).encrypt, + GOST3412Kuznechik.blocksize, + content, + ), + tag_expected, + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EnvelopedData."), + ) + + def test_kari_static(self): + ci = ContentInfo().decod(b64decode(""" +MIIBawYJKoZIhvcNAQcDoIIBXDCCAVgCAQIxgfehgfQCAQOgQjBAMDgxDTALBgNV +BAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJp +dAIEAYy6gqEiBCBvcfyuSF57y8vVyaw8Z0ch3wjC4lPKTrpVRXty4Rhk5DAXBgkq +hQMHAQEHAQEwCgYIKoUDBwEBBgEwbjBsMEAwODENMAsGA1UEChMEVEsyNjEnMCUG +A1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0AgQBjLqDBChPbi6B +krXuLPexPAL2oUGCFWDGQHqINL5ExuMBG7/5XQRqriKARVa0MFkGCSqGSIb3DQEH +ATAbBgkqhQMHAQEFAQEwDgQMdNdCKnYAAAAwqTEDgC9O2bYyTGQJ8WUQGq0zHwzX +L0jFhWHTF1tcAxYmd9pX5i89UwIxhtYqyjX1QHju2g== +""")) + _, ed = ci["content"].defined + kari = ed["recipientInfos"][0]["kari"] + ukm = bytes(kari["ukm"]) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_wrap_kexp15, + ) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_256, + ) + kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"]) + keymat = keg( + self.curve256, + self.recipient256_prv, + self.sender256_pub, + ukm, + ) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Magma(kek).encrypt, + GOST3412Magma(kim).encrypt, + GOST3412Magma.blocksize, + kexp, + ukm[24:24 + GOST3412Magma.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_ctracpkm, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + content = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(cek).encrypt, + 8 * 1024, + GOST3412Magma.blocksize, + bytes(eci["encryptedContent"]), + eci_ukm[:GOST3412Magma.blocksize // 2], + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EnvelopedData."), + ) + + def test_ktri_256(self): + ci = ContentInfo().decod(b64decode(""" +MIIBlQYJKoZIhvcNAQcDoIIBhjCCAYICAQAxggEcMIIBGAIBADBAMDgxDTALBgNV +BAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJp +dAIEAYy6gzAXBgkqhQMHAQEHAgEwCgYIKoUDBwEBBgEEgbcwgbQEMFiMredFR3Mv +3g2wqyVXRnrhYEBMNFaqqgBpHwPQh3bF98tt9HZPxRDCww0OPfxeuTBeMBcGCCqF +AwcBAQEBMAsGCSqFAwcBAgEBAQNDAARAdFJ9ww+3ptvQiaQpizCldNYhl4DB1rl8 +Fx/2FIgnwssCbYRQ+UuRsTk9dfLLTGJG3JIEXKFxXWBgOrK965A5pAQg9f2/EHxG +DfetwCe1a6uUDCWD+wp5dYOpfkry8YRDEJgwXQYJKoZIhvcNAQcBMB8GCSqFAwcB +AQUCATASBBDUHNxmVclO/v3OaY9P7jxOgC+sD9CHGlEMRUpfGn6yfFDMExmYeby8 +LzdPJe1MkYV0qQgdC1zI3nQ7/4taf+4zRA== +""")) + _, ed = ci["content"].defined + ktri = ed["recipientInfos"][0]["ktri"] + self.assertEqual( + ktri["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_wrap_kexp15, + ) + self.assertEqual( + ktri["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_256, + ) + _, encrypted_key = ktri["encryptedKey"].defined + self.assertEqual( + encrypted_key["ephemeralPublicKey"]["algorithm"]["algorithm"], + id_tc26_gost3410_2012_256, + ) + pub = pub_unmarshal(bytes(OctetString().decod( + bytes(encrypted_key["ephemeralPublicKey"]["subjectPublicKey"]) + ))) + ukm = bytes(encrypted_key["ukm"]) + kexp = bytes(encrypted_key["encryptedKey"]) + keymat = keg(self.curve256, self.recipient256_prv, pub, ukm) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Kuznechik(kek).encrypt, + GOST3412Kuznechik(kim).encrypt, + GOST3412Kuznechik.blocksize, + kexp, + ukm[24:24 + GOST3412Kuznechik.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_ctracpkm, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + content = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(cek).encrypt, + 256 * 1024, + GOST3412Kuznechik.blocksize, + bytes(eci["encryptedContent"]), + eci_ukm[:GOST3412Kuznechik.blocksize // 2], + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EnvelopedData."), + ) + + def test_ktri_512(self): + ci = ContentInfo().decod(b64decode(""" +MIIB5wYJKoZIhvcNAQcDoIIB2DCCAdQCAQAxggFXMIIBUwIBADBAMDgxDTALBgNVBAoTBFRL +MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdAIEAYy6hTAXBgkq +hQMHAQEHAQEwCgYIKoUDBwEBBgIEgfIwge8EKDof9JLTJVuIfP+c+imDCGyOLtAYENkoXpeU +CdiGn0Lt65t3TN9G0bUwgaAwFwYIKoUDBwEBAQIwCwYJKoUDBwECAQIBA4GEAASBgDD9XXHn +0j4EwY3DGB1wzHeThPRDlCwIvpmqWy00QDhS3fLRWiETSe9uMLeg27zI/EiserKMasNZum/i +d09cmP8aTNIDNRtI5H9M0mH7LpEtY8L901MszvOKHLDYdemvz0JUqOvBtvoeQ6sV4Gl45zXx +HTzBWlBw1FLX/ITWLapaBCAa09foTeA+PObBznGuCOPoKy+xz/9IIVmZidI6EYkIrzBZBgkq +hkiG9w0BBwEwGwYJKoUDBwEBBQECMA4EDA4z1UwRL4WYzKFX/oAv8eEX3fWt6hxDpjO0rI7/ +CiJ/CwYGCKODJ9h63vAwlsWwcPwAjxcsLvCNlv6i4NqhGTAXBgkqhQMHAQAGAQExCgQIs2DT +LuZ22Yw= +""")) + _, ed = ci["content"].defined + ktri = ed["recipientInfos"][0]["ktri"] + self.assertEqual( + ktri["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_wrap_kexp15, + ) + self.assertEqual( + ktri["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_512, + ) + _, encrypted_key = ktri["encryptedKey"].defined + self.assertEqual( + encrypted_key["ephemeralPublicKey"]["algorithm"]["algorithm"], + id_tc26_gost3410_2012_512, + ) + pub = pub_unmarshal( + bytes(OctetString().decod( + bytes(encrypted_key["ephemeralPublicKey"]["subjectPublicKey"]) + )), + ) + ukm = bytes(encrypted_key["ukm"]) + kexp = bytes(encrypted_key["encryptedKey"]) + keymat = keg(self.curve512, self.recipient512_prv, pub, ukm) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Magma(kek).encrypt, + GOST3412Magma(kim).encrypt, + GOST3412Magma.blocksize, + kexp, + ukm[24:24 + GOST3412Magma.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_ctracpkm_omac, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + self.assertEqual(ed["unprotectedAttrs"][0]["attrType"], id_cms_mac_attr) + encrypted_mac = bytes(ed["unprotectedAttrs"][0]["attrValues"][0].defined[1]) + encrypted_content = bytes(eci["encryptedContent"]) + cek_enc, cek_mac = kdf_tree_gostr3411_2012_256( + cek, b"kdf tree", eci_ukm[GOST3412Magma.blocksize // 2:], 2, + ) + content_and_tag = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(cek_enc).encrypt, + 8 * 1024, + GOST3412Magma.blocksize, + encrypted_content + encrypted_mac, + eci_ukm[:GOST3412Magma.blocksize // 2], + ) + content = content_and_tag[:-GOST3412Magma.blocksize] + tag_expected = content_and_tag[-GOST3412Magma.blocksize:] + self.assertSequenceEqual( + omac( + GOST3412Magma(cek_mac).encrypt, + GOST3412Magma.blocksize, + content, + ), + tag_expected, + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EnvelopedData."), + ) + + def test_digested256(self): + ci = ContentInfo().decod(b64decode(""" +MH0GCSqGSIb3DQEHBaBwMG4CAQAwCgYIKoUDBwEBAgIwOwYJKoZIhvcNAQcBoC4ELMru7fLw +7uv87fvpIO/w6Ozl8CDk6/8g8fLw8+ry8/D7IERpZ2VzdERhdGEuBCD/esPQYsGkzxZV8uUM +IAWt6SI8KtxBP8NyG8AGbJ8i/Q== +""")) + _, dd = ci["content"].defined + eci = dd["encapContentInfo"] + self.assertSequenceEqual( + GOST34112012256(bytes(eci["eContent"])).digest(), + bytes(dd["digest"]), + ) + + def test_digested512(self): + ci = ContentInfo().decod(b64decode(""" +MIGfBgkqhkiG9w0BBwWggZEwgY4CAQAwCgYIKoUDBwEBAgMwOwYJKoZIhvcNAQcBoC4ELMru +7fLw7uv87fvpIO/w6Ozl8CDk6/8g8fLw8+ry8/D7IERpZ2VzdERhdGEuBEDe4VUvcKSRvU7R +FVhFjajXY+nJSUkUsoi3oOeJBnru4PErt8RusPrCJs614ciHCM+ehrC4a+M1Nbq77F/Wsa/v +""")) + _, dd = ci["content"].defined + eci = dd["encapContentInfo"] + self.assertSequenceEqual( + GOST34112012512(bytes(eci["eContent"])).digest(), + bytes(dd["digest"]), + ) + + def test_encrypted_kuznechik(self): + ci = ContentInfo().decod(b64decode(""" +MHEGCSqGSIb3DQEHBqBkMGICAQAwXQYJKoZIhvcNAQcBMB8GCSqFAwcBAQUCATASBBBSwX+z +yOEPPuGyfpsRG4AigC/P8ftTdQMStfIThVkE/vpJlwaHgGv83m2bsPayeyuqpoTeEMOaqGcO +0MxHWsC9hQ== +""")) + _, ed = ci["content"].defined + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_ctracpkm, + ) + ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + content = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(self.psk).encrypt, + 256 * 1024, + GOST3412Kuznechik.blocksize, + bytes(eci["encryptedContent"]), + ukm[:GOST3412Kuznechik.blocksize // 2], + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EncryptedData."), + ) + + def test_encrypted_magma(self): + ci = ContentInfo().decod(b64decode(""" +MIGIBgkqhkiG9w0BBwagezB5AgEAMFkGCSqGSIb3DQEHATAbBgkqhQMHAQEFAQIwDgQMuncO +u3uYPbI30vFCgC9Nsws4R09yLp6jUtadncWUPZGmCGpPKnXGgNHvEmUArgKJvu4FPHtLkHuL +eQXZg6EZMBcGCSqFAwcBAAYBATEKBAjCbQoH632oGA== +""")) + _, ed = ci["content"].defined + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_ctracpkm_omac, + ) + ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + self.assertEqual(ed["unprotectedAttrs"][0]["attrType"], id_cms_mac_attr) + encrypted_mac = bytes(ed["unprotectedAttrs"][0]["attrValues"][0].defined[1]) + cek_enc, cek_mac = kdf_tree_gostr3411_2012_256( + self.psk, b"kdf tree", ukm[GOST3412Magma.blocksize // 2:], 2, + ) + content_and_tag = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(cek_enc).encrypt, + 8 * 1024, + GOST3412Magma.blocksize, + bytes(eci["encryptedContent"]) + encrypted_mac, + ukm[:GOST3412Magma.blocksize // 2], + ) + content = content_and_tag[:-GOST3412Magma.blocksize] + tag_expected = content_and_tag[-GOST3412Magma.blocksize:] + self.assertSequenceEqual( + omac( + GOST3412Magma(cek_mac).encrypt, + GOST3412Magma.blocksize, + content, + ), + tag_expected, + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EncryptedData."), + ) diff --git a/pygost-5.13/build/lib/pygost/test_gost28147.py b/pygost-5.13/build/lib/pygost/test_gost28147.py new file mode 100644 index 0000000..a83e930 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_gost28147.py @@ -0,0 +1,384 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from unittest import TestCase + +from pygost.gost28147 import block2ns +from pygost.gost28147 import BLOCKSIZE +from pygost.gost28147 import cbc_decrypt +from pygost.gost28147 import cbc_encrypt +from pygost.gost28147 import cfb_decrypt +from pygost.gost28147 import cfb_encrypt +from pygost.gost28147 import cnt +from pygost.gost28147 import DEFAULT_SBOX +from pygost.gost28147 import ecb_decrypt +from pygost.gost28147 import ecb_encrypt +from pygost.gost28147 import encrypt +from pygost.gost28147 import KEYSIZE +from pygost.gost28147 import MESH_MAX_DATA +from pygost.gost28147 import ns2block +from pygost.utils import hexdec +from pygost.utils import strxor + + +class ECBTest(TestCase): + def test_gcl(self): + """Test vectors from libgcl3 + """ + sbox = "id-Gost28147-89-TestParamSet" + key = hexdec(b"0475f6e05038fbfad2c7c390edb3ca3d1547124291ae1e8a2f79cd9ed2bcefbd") + plaintext = bytes(bytearray(( + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, + 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, + 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, + 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, + 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, + 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, + 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, + 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, + 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, + 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, + 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, + 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, + 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, + 0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, + 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, + 0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, + 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, + 0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8, + 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, + 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + ))) + ciphertext = bytes(bytearray(( + 0x4b, 0x8c, 0x4c, 0x98, 0x15, 0xf2, 0x4a, 0xea, + 0x1e, 0xc3, 0x57, 0x09, 0xb3, 0xbc, 0x2e, 0xd1, + 0xe0, 0xd1, 0xf2, 0x22, 0x65, 0x2d, 0x59, 0x18, + 0xf7, 0xdf, 0xfc, 0x80, 0x4b, 0xde, 0x5c, 0x68, + 0x46, 0x53, 0x75, 0x53, 0xa7, 0x46, 0x0d, 0xec, + 0x05, 0x1f, 0x1b, 0xd3, 0x0a, 0x63, 0x1a, 0xb7, + 0x78, 0xc4, 0x43, 0xe0, 0x5d, 0x3e, 0xa4, 0x0e, + 0x2d, 0x7e, 0x23, 0xa9, 0x1b, 0xc9, 0x02, 0xbc, + 0x21, 0x0c, 0x84, 0xcb, 0x0d, 0x0a, 0x07, 0xc8, + 0x7b, 0xd0, 0xfb, 0xb5, 0x1a, 0x14, 0x04, 0x5c, + 0xa2, 0x53, 0x97, 0x71, 0x2e, 0x5c, 0xc2, 0x8f, + 0x39, 0x3f, 0x6f, 0x52, 0xf2, 0x30, 0x26, 0x4e, + 0x8c, 0xe0, 0xd1, 0x01, 0x75, 0x6d, 0xdc, 0xd3, + 0x03, 0x79, 0x1e, 0xca, 0xd5, 0xc1, 0x0e, 0x12, + 0x53, 0x0a, 0x78, 0xe2, 0x0a, 0xb1, 0x1c, 0xea, + 0x3a, 0xf8, 0x55, 0xb9, 0x7c, 0xe1, 0x0b, 0xba, + 0xa0, 0xc8, 0x96, 0xeb, 0x50, 0x5a, 0xd3, 0x60, + 0x43, 0xa3, 0x0f, 0x98, 0xdb, 0xd9, 0x50, 0x6d, + 0x63, 0x91, 0xaf, 0x01, 0x40, 0xe9, 0x75, 0x5a, + 0x46, 0x5c, 0x1f, 0x19, 0x4a, 0x0b, 0x89, 0x9b, + 0xc4, 0xf6, 0xf8, 0xf5, 0x2f, 0x87, 0x3f, 0xfa, + 0x26, 0xd4, 0xf8, 0x25, 0xba, 0x1f, 0x98, 0x82, + 0xfc, 0x26, 0xaf, 0x2d, 0xc0, 0xf9, 0xc4, 0x58, + 0x49, 0xfa, 0x09, 0x80, 0x02, 0x62, 0xa4, 0x34, + 0x2d, 0xcb, 0x5a, 0x6b, 0xab, 0x61, 0x5d, 0x08, + 0xd4, 0x26, 0xe0, 0x08, 0x13, 0xd6, 0x2e, 0x02, + 0x2a, 0x37, 0xe8, 0xd0, 0xcf, 0x36, 0xf1, 0xc7, + 0xc0, 0x3f, 0x9b, 0x21, 0x60, 0xbd, 0x29, 0x2d, + 0x2e, 0x01, 0x48, 0x4e, 0xf8, 0x8f, 0x20, 0x16, + 0x8a, 0xbf, 0x82, 0xdc, 0x32, 0x7a, 0xa3, 0x18, + 0x69, 0xd1, 0x50, 0x59, 0x31, 0x91, 0xf2, 0x6c, + 0x5a, 0x5f, 0xca, 0x58, 0x9a, 0xb2, 0x2d, 0xb2, + ))) + encrypted = ecb_encrypt(key, plaintext, sbox=sbox) + self.assertSequenceEqual(encrypted, ciphertext) + decrypted = ecb_decrypt(key, encrypted, sbox=sbox) + self.assertSequenceEqual(decrypted, plaintext) + + def test_cryptopp(self): + """Test vectors from Crypto++ 5.6.2 + """ + sbox = "AppliedCryptography" + data = ( + (b"BE5EC2006CFF9DCF52354959F1FF0CBFE95061B5A648C10387069C25997C0672", b"0DF82802B741A292", b"07F9027DF7F7DF89"), + (b"B385272AC8D72A5A8B344BC80363AC4D09BF58F41F540624CBCB8FDCF55307D7", b"1354EE9C0A11CD4C", b"4FB50536F960A7B1"), + (b"AEE02F609A35660E4097E546FD3026B032CD107C7D459977ADF489BEF2652262", b"6693D492C4B0CC39", b"670034AC0FA811B5"), + (b"320E9D8422165D58911DFC7D8BBB1F81B0ECD924023BF94D9DF7DCF7801240E0", b"99E2D13080928D79", b"8118FF9D3B3CFE7D"), + (b"C9F703BBBFC63691BFA3B7B87EA8FD5E8E8EF384EF733F1A61AEF68C8FFA265F", b"D1E787749C72814C", b"A083826A790D3E0C"), + (b"728FEE32F04B4C654AD7F607D71C660C2C2670D7C999713233149A1C0C17A1F0", b"D4C05323A4F7A7B5", b"4D1F2E6B0D9DE2CE"), + (b"35FC96402209500FCFDEF5352D1ABB038FE33FC0D9D58512E56370B22BAA133B", b"8742D9A05F6A3AF6", b"2F3BB84879D11E52"), + (b"D416F630BE65B7FE150656183370E07018234EE5DA3D89C4CE9152A03E5BFB77", b"F86506DA04E41CB8", b"96F0A5C77A04F5CE"), + ) + for key, pt, ct in data: + key = hexdec(key) + pt = hexdec(pt) + ct = hexdec(ct) + self.assertSequenceEqual(ecb_encrypt(key, pt, sbox=sbox), ct) + + def test_cryptomanager(self): + """Test vector from http://cryptomanager.com/tv.html + """ + sbox = "id-GostR3411-94-TestParamSet" + key = hexdec(b"75713134B60FEC45A607BB83AA3746AF4FF99DA6D1B53B5B1B402A1BAA030D1B") + self.assertSequenceEqual( + ecb_encrypt(key, hexdec(b"1122334455667788"), sbox=sbox), + hexdec(b"03251E14F9D28ACB"), + ) + + +class CFBTest(TestCase): + def test_cryptomanager(self): + """Test vector from http://cryptomanager.com/tv.html + """ + key = hexdec(b"75713134B60FEC45A607BB83AA3746AF4FF99DA6D1B53B5B1B402A1BAA030D1B") + sbox = "id-GostR3411-94-TestParamSet" + self.assertSequenceEqual( + cfb_encrypt( + key, + hexdec(b"112233445566778899AABBCCDD800000"), + iv=hexdec(b"0102030405060708"), + sbox=sbox, + ), + hexdec(b"6EE84586DD2BCA0CAD3616940E164242"), + ) + self.assertSequenceEqual( + cfb_decrypt( + key, + hexdec(b"6EE84586DD2BCA0CAD3616940E164242"), + iv=hexdec(b"0102030405060708"), + sbox=sbox, + ), + hexdec(b"112233445566778899AABBCCDD800000"), + ) + + def test_steps(self): + """Check step-by-step operation manually + """ + key = urandom(KEYSIZE) + iv = urandom(BLOCKSIZE) + plaintext = urandom(20) + ciphertext = cfb_encrypt(key, plaintext, iv) + + # First full block + step = encrypt(DEFAULT_SBOX, key, block2ns(iv)) + step = strxor(plaintext[:8], ns2block(step)) + self.assertSequenceEqual(step, ciphertext[:8]) + + # Second full block + step = encrypt(DEFAULT_SBOX, key, block2ns(step)) + step = strxor(plaintext[8:16], ns2block(step)) + self.assertSequenceEqual(step, ciphertext[8:16]) + + # Third non-full block + step = encrypt(DEFAULT_SBOX, key, block2ns(step)) + step = strxor(plaintext[16:] + 4 * b"\x00", ns2block(step)) + self.assertSequenceEqual(step[:4], ciphertext[16:]) + + def test_random(self): + """Random data with various sizes + """ + key = urandom(KEYSIZE) + iv = urandom(BLOCKSIZE) + for size in (5, 8, 16, 120): + pt = urandom(size) + self.assertSequenceEqual( + cfb_decrypt(key, cfb_encrypt(key, pt, iv), iv), + pt, + ) + + +class CTRTest(TestCase): + def test_gcl(self): + """Test vectors from libgcl3 + """ + sbox = "id-Gost28147-89-TestParamSet" + key = hexdec(b"0475f6e05038fbfad2c7c390edb3ca3d1547124291ae1e8a2f79cd9ed2bcefbd") + plaintext = bytes(bytearray(( + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, + 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, + 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, + 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, + 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, + 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, + 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, + 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, + 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, + 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, + 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, + 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, + 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, + 0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, + 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, + 0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, + 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, + 0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8, + 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, + 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, + ))) + ciphertext = bytes(bytearray(( + 0x4a, 0x5e, 0x37, 0x6c, 0xa1, 0x12, 0xd3, 0x55, + 0x09, 0x13, 0x1a, 0x21, 0xac, 0xfb, 0xb2, 0x1e, + 0x8c, 0x24, 0x9b, 0x57, 0x20, 0x68, 0x46, 0xd5, + 0x23, 0x2a, 0x26, 0x35, 0x12, 0x56, 0x5c, 0x69, + 0x2a, 0x2f, 0xd1, 0xab, 0xbd, 0x45, 0xdc, 0x3a, + 0x1a, 0xa4, 0x57, 0x64, 0xd5, 0xe4, 0x69, 0x6d, + 0xb4, 0x8b, 0xf1, 0x54, 0x78, 0x3b, 0x10, 0x8f, + 0x7a, 0x4b, 0x32, 0xe0, 0xe8, 0x4c, 0xbf, 0x03, + 0x24, 0x37, 0x95, 0x6a, 0x55, 0xa8, 0xce, 0x6f, + 0x95, 0x62, 0x12, 0xf6, 0x79, 0xe6, 0xf0, 0x1b, + 0x86, 0xef, 0x36, 0x36, 0x05, 0xd8, 0x6f, 0x10, + 0xa1, 0x41, 0x05, 0x07, 0xf8, 0xfa, 0xa4, 0x0b, + 0x17, 0x2c, 0x71, 0xbc, 0x8b, 0xcb, 0xcf, 0x3d, + 0x74, 0x18, 0x32, 0x0b, 0x1c, 0xd2, 0x9e, 0x75, + 0xba, 0x3e, 0x61, 0xe1, 0x61, 0x96, 0xd0, 0xee, + 0x8f, 0xf2, 0x9a, 0x5e, 0xb7, 0x7a, 0x15, 0xaa, + 0x4e, 0x1e, 0x77, 0x7c, 0x99, 0xe1, 0x41, 0x13, + 0xf4, 0x60, 0x39, 0x46, 0x4c, 0x35, 0xde, 0x95, + 0xcc, 0x4f, 0xd5, 0xaf, 0xd1, 0x4d, 0x84, 0x1a, + 0x45, 0xc7, 0x2a, 0xf2, 0x2c, 0xc0, 0xb7, 0x94, + 0xa3, 0x08, 0xb9, 0x12, 0x96, 0xb5, 0x97, 0x99, + 0x3a, 0xb7, 0x0c, 0x14, 0x56, 0xb9, 0xcb, 0x49, + 0x44, 0xa9, 0x93, 0xa9, 0xfb, 0x19, 0x10, 0x8c, + 0x6a, 0x68, 0xe8, 0x7b, 0x06, 0x57, 0xf0, 0xef, + 0x88, 0x44, 0xa6, 0xd2, 0x98, 0xbe, 0xd4, 0x07, + 0x41, 0x37, 0x45, 0xa6, 0x71, 0x36, 0x76, 0x69, + 0x4b, 0x75, 0x15, 0x33, 0x90, 0x29, 0x6e, 0x33, + 0xcb, 0x96, 0x39, 0x78, 0x19, 0x2e, 0x96, 0xf3, + 0x49, 0x4c, 0x89, 0x3d, 0xa1, 0x86, 0x82, 0x00, + 0xce, 0xbd, 0x54, 0x29, 0x65, 0x00, 0x1d, 0x16, + 0x13, 0xc3, 0xfe, 0x1f, 0x8c, 0x55, 0x63, 0x09, + 0x1f, 0xcd, 0xd4, 0x28, 0xca, + ))) + iv = b"\x02\x01\x01\x01\x01\x01\x01\x01" + encrypted = cnt(key, plaintext, iv=iv, sbox=sbox) + self.assertSequenceEqual(encrypted, ciphertext) + decrypted = cnt(key, encrypted, iv=iv, sbox=sbox) + self.assertSequenceEqual(decrypted, plaintext) + + def test_gcl2(self): + """Test vectors 2 from libgcl3 + """ + sbox = "id-Gost28147-89-TestParamSet" + key = hexdec(b"fc7ad2886f455b50d29008fa622b57d5c65b3c637202025799cadf0768519e8a") + plaintext = bytes(bytearray(( + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, + 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, + ))) + ciphertext = bytes(bytearray(( + 0xd0, 0xbe, 0x60, 0x1a, 0x2c, 0xf1, 0x90, 0x26, + 0x9b, 0x7b, 0x23, 0xb4, 0xd2, 0xcc, 0xe1, 0x15, + 0xf6, 0x05, 0x57, 0x28, 0x88, 0x75, 0xeb, 0x1e, + 0xd3, 0x62, 0xdc, 0xda, 0x9b, 0x62, 0xee, 0x9a, + 0x57, 0x87, 0x8a, 0xf1, 0x82, 0x37, 0x9c, 0x7f, + 0x13, 0xcc, 0x55, 0x38, 0xb5, 0x63, 0x32, 0xc5, + 0x23, 0xa4, 0xcb, 0x7d, 0x51, + ))) + iv = BLOCKSIZE * b"\x00" + encrypted = cnt(key, plaintext, iv=iv, sbox=sbox) + self.assertSequenceEqual(encrypted, ciphertext) + decrypted = cnt(key, encrypted, iv=iv, sbox=sbox) + self.assertSequenceEqual(decrypted, plaintext) + + +class CBCTest(TestCase): + def test_pad_requirement(self): + key = KEYSIZE * b"x" + for s in (b"", b"foo", b"foobarbaz"): + with self.assertRaises(ValueError): + cbc_encrypt(key, s, pad=False) + with self.assertRaises(ValueError): + cbc_decrypt(key, s, pad=False) + + def test_passes(self): + iv = urandom(BLOCKSIZE) + key = KEYSIZE * b"x" + for pt in (b"foo", b"foobarba", b"foobarbaz", 16 * b"x"): + ct = cbc_encrypt(key, pt, iv) + dt = cbc_decrypt(key, ct) + self.assertSequenceEqual(pt, dt) + + def test_iv_existence_check(self): + key = KEYSIZE * b"x" + with self.assertRaises(ValueError): + cbc_decrypt(key, BLOCKSIZE * b"x") + iv = urandom(BLOCKSIZE) + cbc_decrypt(key, cbc_encrypt(key, BLOCKSIZE * b"x", iv)) + + def test_meshing(self): + pt = urandom(MESH_MAX_DATA * 3) + key = urandom(KEYSIZE) + ct = cbc_encrypt(key, pt) + dt = cbc_decrypt(key, ct) + self.assertSequenceEqual(pt, dt) + + +class CFBMeshingTest(TestCase): + def setUp(self): + self.key = urandom(KEYSIZE) + self.iv = urandom(BLOCKSIZE) + + def test_single(self): + pt = b"\x00" + ct = cfb_encrypt(self.key, pt, mesh=True) + dec = cfb_decrypt(self.key, ct, mesh=True) + self.assertSequenceEqual(pt, dec) + + def test_short(self): + pt = urandom(MESH_MAX_DATA - 1) + ct = cfb_encrypt(self.key, pt, mesh=True) + dec = cfb_decrypt(self.key, ct, mesh=True) + dec_plain = cfb_decrypt(self.key, ct) + self.assertSequenceEqual(pt, dec) + self.assertSequenceEqual(pt, dec_plain) + + def test_short_iv(self): + pt = urandom(MESH_MAX_DATA - 1) + ct = cfb_encrypt(self.key, pt, iv=self.iv, mesh=True) + dec = cfb_decrypt(self.key, ct, iv=self.iv, mesh=True) + dec_plain = cfb_decrypt(self.key, ct, iv=self.iv) + self.assertSequenceEqual(pt, dec) + self.assertSequenceEqual(pt, dec_plain) + + def test_longer_iv(self): + pt = urandom(MESH_MAX_DATA * 3) + ct = cfb_encrypt(self.key, pt, iv=self.iv, mesh=True) + dec = cfb_decrypt(self.key, ct, iv=self.iv, mesh=True) + dec_plain = cfb_decrypt(self.key, ct, iv=self.iv) + self.assertSequenceEqual(pt, dec) + self.assertNotEqual(pt, dec_plain) diff --git a/pygost-5.13/build/lib/pygost/test_gost28147_mac.py b/pygost-5.13/build/lib/pygost/test_gost28147_mac.py new file mode 100644 index 0000000..ec1af3f --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_gost28147_mac.py @@ -0,0 +1,63 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from unittest import TestCase + +from pygost.gost28147_mac import MAC + + +class TestMAC(TestCase): + """Test vectors generated with libgcl3 library + """ + k = b"This is message\xFF length\x0032 bytes" + + def test_a(self): + self.assertSequenceEqual( + MAC(self.k, b"a").hexdigest(), + "bd5d3b5b2b7b57af", + ) + + def test_abc(self): + self.assertSequenceEqual( + MAC(self.k, b"abc").hexdigest(), + "28661e40805b1ff9", + ) + + def test_128U(self): + self.assertSequenceEqual( + MAC(self.k, 128 * b"U").hexdigest(), + "1a06d1bad74580ef", + ) + + def test_13x(self): + self.assertSequenceEqual( + MAC(self.k, 13 * b"x").hexdigest(), + "917ee1f1a668fbd3", + ) + + def test_parts(self): + m = MAC(self.k) + m.update(b"foo") + m.update(b"bar") + self.assertSequenceEqual(m.digest(), MAC(self.k, b"foobar").digest()) + + def test_copy(self): + m = MAC(self.k, b"foo") + c = m.copy() + m.update(b"barbaz") + c.update(b"bar") + c.update(b"baz") + self.assertSequenceEqual(m.digest(), c.digest()) diff --git a/pygost-5.13/build/lib/pygost/test_gost3410.py b/pygost-5.13/build/lib/pygost/test_gost3410.py new file mode 100644 index 0000000..cd71535 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_gost3410.py @@ -0,0 +1,495 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from unittest import TestCase + +from pygost.gost3410 import CURVES +from pygost.gost3410 import GOST3410Curve +from pygost.gost3410 import prv_marshal +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import public_key +from pygost.gost3410 import sign +from pygost.gost3410 import uv2xy +from pygost.gost3410 import verify +from pygost.gost3410 import xy2uv +from pygost.utils import bytes2long +from pygost.utils import hexdec +from pygost.utils import hexenc +from pygost.utils import long2bytes + + +class Test341001(TestCase): + def test_rfc(self): + """Test vector from :rfc:`5832` + """ + prv = bytes(bytearray(( + 0x7A, 0x92, 0x9A, 0xDE, 0x78, 0x9B, 0xB9, 0xBE, + 0x10, 0xED, 0x35, 0x9D, 0xD3, 0x9A, 0x72, 0xC1, + 0x1B, 0x60, 0x96, 0x1F, 0x49, 0x39, 0x7E, 0xEE, + 0x1D, 0x19, 0xCE, 0x98, 0x91, 0xEC, 0x3B, 0x28 + ))) + pub_x = bytes(bytearray(( + 0x7F, 0x2B, 0x49, 0xE2, 0x70, 0xDB, 0x6D, 0x90, + 0xD8, 0x59, 0x5B, 0xEC, 0x45, 0x8B, 0x50, 0xC5, + 0x85, 0x85, 0xBA, 0x1D, 0x4E, 0x9B, 0x78, 0x8F, + 0x66, 0x89, 0xDB, 0xD8, 0xE5, 0x6F, 0xD8, 0x0B + ))) + pub_y = bytes(bytearray(( + 0x26, 0xF1, 0xB4, 0x89, 0xD6, 0x70, 0x1D, 0xD1, + 0x85, 0xC8, 0x41, 0x3A, 0x97, 0x7B, 0x3C, 0xBB, + 0xAF, 0x64, 0xD1, 0xC5, 0x93, 0xD2, 0x66, 0x27, + 0xDF, 0xFB, 0x10, 0x1A, 0x87, 0xFF, 0x77, 0xDA + ))) + digest = bytes(bytearray(( + 0x2D, 0xFB, 0xC1, 0xB3, 0x72, 0xD8, 0x9A, 0x11, + 0x88, 0xC0, 0x9C, 0x52, 0xE0, 0xEE, 0xC6, 0x1F, + 0xCE, 0x52, 0x03, 0x2A, 0xB1, 0x02, 0x2E, 0x8E, + 0x67, 0xEC, 0xE6, 0x67, 0x2B, 0x04, 0x3E, 0xE5 + ))) + signature = bytes(bytearray(( + 0x41, 0xAA, 0x28, 0xD2, 0xF1, 0xAB, 0x14, 0x82, + 0x80, 0xCD, 0x9E, 0xD5, 0x6F, 0xED, 0xA4, 0x19, + 0x74, 0x05, 0x35, 0x54, 0xA4, 0x27, 0x67, 0xB8, + 0x3A, 0xD0, 0x43, 0xFD, 0x39, 0xDC, 0x04, 0x93, + 0x01, 0x45, 0x6C, 0x64, 0xBA, 0x46, 0x42, 0xA1, + 0x65, 0x3C, 0x23, 0x5A, 0x98, 0xA6, 0x02, 0x49, + 0xBC, 0xD6, 0xD3, 0xF7, 0x46, 0xB6, 0x31, 0xDF, + 0x92, 0x80, 0x14, 0xF6, 0xC5, 0xBF, 0x9C, 0x40 + ))) + prv = bytes2long(prv) + signature = signature[32:] + signature[:32] + + c = CURVES["id-GostR3410-2001-TestParamSet"] + pubX, pubY = public_key(c, prv) + self.assertSequenceEqual(long2bytes(pubX), pub_x) + self.assertSequenceEqual(long2bytes(pubY), pub_y) + s = sign(c, prv, digest) + self.assertTrue(verify(c, (pubX, pubY), digest, s)) + self.assertTrue(verify(c, (pubX, pubY), digest, signature)) + + def test_sequence(self): + c = CURVES["id-GostR3410-2001-TestParamSet"] + prv = prv_unmarshal(urandom(32)) + pubX, pubY = public_key(c, prv_unmarshal(prv_marshal(c, prv))) + for _ in range(20): + digest = urandom(32) + s = sign(c, prv, digest) + self.assertTrue(verify(c, (pubX, pubY), digest, s)) + + +class Test34102012(TestCase): + def test_1(self): + """Test vector from 34.10-2012 standard itself + """ + curve = CURVES["id-GostR3410-2001-TestParamSet"] + prv = bytes2long(hexdec("7A929ADE789BB9BE10ED359DD39A72C11B60961F49397EEE1D19CE9891EC3B28")) + digest = hexdec("2DFBC1B372D89A1188C09C52E0EEC61FCE52032AB1022E8E67ECE6672B043EE5") + rand = hexdec("77105C9B20BCD3122823C8CF6FCC7B956DE33814E95B7FE64FED924594DCEAB3") + signature = sign(curve, prv, digest, rand) + r = "41aa28d2f1ab148280cd9ed56feda41974053554a42767b83ad043fd39dc0493" + s = "01456c64ba4642a1653c235a98a60249bcd6d3f746b631df928014f6c5bf9c40" + self.assertSequenceEqual(hexenc(signature), s + r) + + def test_2(self): + """Test vector from 34.10-2012 standard itself + """ + curve = GOST3410Curve( + p=3623986102229003635907788753683874306021320925534678605086546150450856166624002482588482022271496854025090823603058735163734263822371964987228582907372403, + q=3623986102229003635907788753683874306021320925534678605086546150450856166623969164898305032863068499961404079437936585455865192212970734808812618120619743, + a=7, + b=1518655069210828534508950034714043154928747527740206436194018823352809982443793732829756914785974674866041605397883677596626326413990136959047435811826396, + x=1928356944067022849399309401243137598997786635459507974357075491307766592685835441065557681003184874819658004903212332884252335830250729527632383493573274, + y=2288728693371972859970012155529478416353562327329506180314497425931102860301572814141997072271708807066593850650334152381857347798885864807605098724013854, + ) + prv = bytes2long(hexdec("0BA6048AADAE241BA40936D47756D7C93091A0E8514669700EE7508E508B102072E8123B2200A0563322DAD2827E2714A2636B7BFD18AADFC62967821FA18DD4")) + digest = hexdec("3754F3CFACC9E0615C4F4A7C4D8DAB531B09B6F9C170C533A71D147035B0C5917184EE536593F4414339976C647C5D5A407ADEDB1D560C4FC6777D2972075B8C") + rand = hexdec("0359E7F4B1410FEACC570456C6801496946312120B39D019D455986E364F365886748ED7A44B3E794434006011842286212273A6D14CF70EA3AF71BB1AE679F1") + signature = sign(curve, prv, digest, rand) + r = "2f86fa60a081091a23dd795e1e3c689ee512a3c82ee0dcc2643c78eea8fcacd35492558486b20f1c9ec197c90699850260c93bcbcd9c5c3317e19344e173ae36" + s = "1081b394696ffe8e6585e7a9362d26b6325f56778aadbc081c0bfbe933d52ff5823ce288e8c4f362526080df7f70ce406a6eeb1f56919cb92a9853bde73e5b4a" + self.assertSequenceEqual(hexenc(signature), s + r) + + def test_gcl3(self): + """Test vector from libgcl3 + """ + p = bytes2long(bytes(bytearray(( + 0x45, 0x31, 0xAC, 0xD1, 0xFE, 0x00, 0x23, 0xC7, + 0x55, 0x0D, 0x26, 0x7B, 0x6B, 0x2F, 0xEE, 0x80, + 0x92, 0x2B, 0x14, 0xB2, 0xFF, 0xB9, 0x0F, 0x04, + 0xD4, 0xEB, 0x7C, 0x09, 0xB5, 0xD2, 0xD1, 0x5D, + 0xF1, 0xD8, 0x52, 0x74, 0x1A, 0xF4, 0x70, 0x4A, + 0x04, 0x58, 0x04, 0x7E, 0x80, 0xE4, 0x54, 0x6D, + 0x35, 0xB8, 0x33, 0x6F, 0xAC, 0x22, 0x4D, 0xD8, + 0x16, 0x64, 0xBB, 0xF5, 0x28, 0xBE, 0x63, 0x73, + )))) + q = bytes2long(bytes(bytearray(( + 0x45, 0x31, 0xAC, 0xD1, 0xFE, 0x00, 0x23, 0xC7, + 0x55, 0x0D, 0x26, 0x7B, 0x6B, 0x2F, 0xEE, 0x80, + 0x92, 0x2B, 0x14, 0xB2, 0xFF, 0xB9, 0x0F, 0x04, + 0xD4, 0xEB, 0x7C, 0x09, 0xB5, 0xD2, 0xD1, 0x5D, + 0xA8, 0x2F, 0x2D, 0x7E, 0xCB, 0x1D, 0xBA, 0xC7, + 0x19, 0x90, 0x5C, 0x5E, 0xEC, 0xC4, 0x23, 0xF1, + 0xD8, 0x6E, 0x25, 0xED, 0xBE, 0x23, 0xC5, 0x95, + 0xD6, 0x44, 0xAA, 0xF1, 0x87, 0xE6, 0xE6, 0xDF, + )))) + a = bytes2long(bytes(bytearray(( + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + )))) + b = bytes2long(bytes(bytearray(( + 0x1C, 0xFF, 0x08, 0x06, 0xA3, 0x11, 0x16, 0xDA, + 0x29, 0xD8, 0xCF, 0xA5, 0x4E, 0x57, 0xEB, 0x74, + 0x8B, 0xC5, 0xF3, 0x77, 0xE4, 0x94, 0x00, 0xFD, + 0xD7, 0x88, 0xB6, 0x49, 0xEC, 0xA1, 0xAC, 0x43, + 0x61, 0x83, 0x40, 0x13, 0xB2, 0xAD, 0x73, 0x22, + 0x48, 0x0A, 0x89, 0xCA, 0x58, 0xE0, 0xCF, 0x74, + 0xBC, 0x9E, 0x54, 0x0C, 0x2A, 0xDD, 0x68, 0x97, + 0xFA, 0xD0, 0xA3, 0x08, 0x4F, 0x30, 0x2A, 0xDC, + )))) + x = bytes2long(bytes(bytearray(( + 0x24, 0xD1, 0x9C, 0xC6, 0x45, 0x72, 0xEE, 0x30, + 0xF3, 0x96, 0xBF, 0x6E, 0xBB, 0xFD, 0x7A, 0x6C, + 0x52, 0x13, 0xB3, 0xB3, 0xD7, 0x05, 0x7C, 0xC8, + 0x25, 0xF9, 0x10, 0x93, 0xA6, 0x8C, 0xD7, 0x62, + 0xFD, 0x60, 0x61, 0x12, 0x62, 0xCD, 0x83, 0x8D, + 0xC6, 0xB6, 0x0A, 0xA7, 0xEE, 0xE8, 0x04, 0xE2, + 0x8B, 0xC8, 0x49, 0x97, 0x7F, 0xAC, 0x33, 0xB4, + 0xB5, 0x30, 0xF1, 0xB1, 0x20, 0x24, 0x8A, 0x9A, + )))) + y = bytes2long(bytes(bytearray(( + 0x2B, 0xB3, 0x12, 0xA4, 0x3B, 0xD2, 0xCE, 0x6E, + 0x0D, 0x02, 0x06, 0x13, 0xC8, 0x57, 0xAC, 0xDD, + 0xCF, 0xBF, 0x06, 0x1E, 0x91, 0xE5, 0xF2, 0xC3, + 0xF3, 0x24, 0x47, 0xC2, 0x59, 0xF3, 0x9B, 0x2C, + 0x83, 0xAB, 0x15, 0x6D, 0x77, 0xF1, 0x49, 0x6B, + 0xF7, 0xEB, 0x33, 0x51, 0xE1, 0xEE, 0x4E, 0x43, + 0xDC, 0x1A, 0x18, 0xB9, 0x1B, 0x24, 0x64, 0x0B, + 0x6D, 0xBB, 0x92, 0xCB, 0x1A, 0xDD, 0x37, 0x1E, + )))) + prv = bytes(bytearray(( + 0x0B, 0xA6, 0x04, 0x8A, 0xAD, 0xAE, 0x24, 0x1B, + 0xA4, 0x09, 0x36, 0xD4, 0x77, 0x56, 0xD7, 0xC9, + 0x30, 0x91, 0xA0, 0xE8, 0x51, 0x46, 0x69, 0x70, + 0x0E, 0xE7, 0x50, 0x8E, 0x50, 0x8B, 0x10, 0x20, + 0x72, 0xE8, 0x12, 0x3B, 0x22, 0x00, 0xA0, 0x56, + 0x33, 0x22, 0xDA, 0xD2, 0x82, 0x7E, 0x27, 0x14, + 0xA2, 0x63, 0x6B, 0x7B, 0xFD, 0x18, 0xAA, 0xDF, + 0xC6, 0x29, 0x67, 0x82, 0x1F, 0xA1, 0x8D, 0xD4, + ))) + pub_x = bytes(bytearray(( + 0x11, 0x5D, 0xC5, 0xBC, 0x96, 0x76, 0x0C, 0x7B, + 0x48, 0x59, 0x8D, 0x8A, 0xB9, 0xE7, 0x40, 0xD4, + 0xC4, 0xA8, 0x5A, 0x65, 0xBE, 0x33, 0xC1, 0x81, + 0x5B, 0x5C, 0x32, 0x0C, 0x85, 0x46, 0x21, 0xDD, + 0x5A, 0x51, 0x58, 0x56, 0xD1, 0x33, 0x14, 0xAF, + 0x69, 0xBC, 0x5B, 0x92, 0x4C, 0x8B, 0x4D, 0xDF, + 0xF7, 0x5C, 0x45, 0x41, 0x5C, 0x1D, 0x9D, 0xD9, + 0xDD, 0x33, 0x61, 0x2C, 0xD5, 0x30, 0xEF, 0xE1, + ))) + pub_y = bytes(bytearray(( + 0x37, 0xC7, 0xC9, 0x0C, 0xD4, 0x0B, 0x0F, 0x56, + 0x21, 0xDC, 0x3A, 0xC1, 0xB7, 0x51, 0xCF, 0xA0, + 0xE2, 0x63, 0x4F, 0xA0, 0x50, 0x3B, 0x3D, 0x52, + 0x63, 0x9F, 0x5D, 0x7F, 0xB7, 0x2A, 0xFD, 0x61, + 0xEA, 0x19, 0x94, 0x41, 0xD9, 0x43, 0xFF, 0xE7, + 0xF0, 0xC7, 0x0A, 0x27, 0x59, 0xA3, 0xCD, 0xB8, + 0x4C, 0x11, 0x4E, 0x1F, 0x93, 0x39, 0xFD, 0xF2, + 0x7F, 0x35, 0xEC, 0xA9, 0x36, 0x77, 0xBE, 0xEC, + ))) + digest = bytes(bytearray(( + 0x37, 0x54, 0xF3, 0xCF, 0xAC, 0xC9, 0xE0, 0x61, + 0x5C, 0x4F, 0x4A, 0x7C, 0x4D, 0x8D, 0xAB, 0x53, + 0x1B, 0x09, 0xB6, 0xF9, 0xC1, 0x70, 0xC5, 0x33, + 0xA7, 0x1D, 0x14, 0x70, 0x35, 0xB0, 0xC5, 0x91, + 0x71, 0x84, 0xEE, 0x53, 0x65, 0x93, 0xF4, 0x41, + 0x43, 0x39, 0x97, 0x6C, 0x64, 0x7C, 0x5D, 0x5A, + 0x40, 0x7A, 0xDE, 0xDB, 0x1D, 0x56, 0x0C, 0x4F, + 0xC6, 0x77, 0x7D, 0x29, 0x72, 0x07, 0x5B, 0x8C, + ))) + signature = bytes(bytearray(( + 0x2F, 0x86, 0xFA, 0x60, 0xA0, 0x81, 0x09, 0x1A, + 0x23, 0xDD, 0x79, 0x5E, 0x1E, 0x3C, 0x68, 0x9E, + 0xE5, 0x12, 0xA3, 0xC8, 0x2E, 0xE0, 0xDC, 0xC2, + 0x64, 0x3C, 0x78, 0xEE, 0xA8, 0xFC, 0xAC, 0xD3, + 0x54, 0x92, 0x55, 0x84, 0x86, 0xB2, 0x0F, 0x1C, + 0x9E, 0xC1, 0x97, 0xC9, 0x06, 0x99, 0x85, 0x02, + 0x60, 0xC9, 0x3B, 0xCB, 0xCD, 0x9C, 0x5C, 0x33, + 0x17, 0xE1, 0x93, 0x44, 0xE1, 0x73, 0xAE, 0x36, + 0x10, 0x81, 0xB3, 0x94, 0x69, 0x6F, 0xFE, 0x8E, + 0x65, 0x85, 0xE7, 0xA9, 0x36, 0x2D, 0x26, 0xB6, + 0x32, 0x5F, 0x56, 0x77, 0x8A, 0xAD, 0xBC, 0x08, + 0x1C, 0x0B, 0xFB, 0xE9, 0x33, 0xD5, 0x2F, 0xF5, + 0x82, 0x3C, 0xE2, 0x88, 0xE8, 0xC4, 0xF3, 0x62, + 0x52, 0x60, 0x80, 0xDF, 0x7F, 0x70, 0xCE, 0x40, + 0x6A, 0x6E, 0xEB, 0x1F, 0x56, 0x91, 0x9C, 0xB9, + 0x2A, 0x98, 0x53, 0xBD, 0xE7, 0x3E, 0x5B, 0x4A, + ))) + prv = bytes2long(prv) + signature = signature[64:] + signature[:64] + c = GOST3410Curve(p, q, a, b, x, y) + pubX, pubY = public_key(c, prv) + self.assertSequenceEqual(long2bytes(pubX), pub_x) + self.assertSequenceEqual(long2bytes(pubY), pub_y) + s = sign(c, prv, digest) + self.assertTrue(verify(c, (pubX, pubY), digest, s)) + self.assertTrue(verify(c, (pubX, pubY), digest, signature)) + + def test_sequence(self): + c = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + prv = bytes2long(urandom(64)) + pubX, pubY = public_key(c, prv_unmarshal(prv_marshal(c, prv))) + for _ in range(20): + digest = urandom(64) + s = sign(c, prv, digest) + self.assertTrue(verify(c, (pubX, pubY), digest, s)) + self.assertNotIn(b"\x00" * 8, s) + + +class TestUVXYConversion(TestCase): + """Twisted Edwards to Weierstrass coordinates conversion and vice versa + """ + def test_curve1(self): + c = CURVES["id-tc26-gost-3410-2012-256-paramSetA"] + u, v = (0x0D, bytes2long(hexdec("60CA1E32AA475B348488C38FAB07649CE7EF8DBE87F22E81F92B2592DBA300E7"))) + self.assertEqual(uv2xy(c, u, v), (c.x, c.y)) + self.assertEqual(xy2uv(c, c.x, c.y), (u, v)) + + def test_curve2(self): + c = CURVES["id-tc26-gost-3410-2012-512-paramSetC"] + u, v = (0x12, bytes2long(hexdec("469AF79D1FB1F5E16B99592B77A01E2A0FDFB0D01794368D9A56117F7B38669522DD4B650CF789EEBF068C5D139732F0905622C04B2BAAE7600303EE73001A3D"))) + self.assertEqual(uv2xy(c, u, v), (c.x, c.y)) + self.assertEqual(xy2uv(c, c.x, c.y), (u, v)) + + +class Test34102012SESPAKE(TestCase): + """Test vectors for multiplication from :rfc:`8133` + """ + def test_curve1(self): + c = CURVES["id-GostR3410-2001-CryptoPro-A-ParamSet"] + q_ind = ( + 0xA69D51CAF1A309FA9E9B66187759B0174C274E080356F23CFCBFE84D396AD7BB, + 0x5D26F29ECC2E9AC0404DCF7986FA55FE94986362170F54B9616426A659786DAC, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F18127067" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x59495655D1E7C7424C622485F575CCF121F3122D274101E8AB734CC9C9A9B45E, + 0x48D1C311D33C9B701F3B03618562A4A07A044E3AF31E3999E67B487778B53C62, + ) + ) + self.assertEqual( + c.exp(0x1F2538097D5A031FA68BBB43C84D12B3DE47B7061C0D5E24993E0C873CDBA6B3), + ( + 0xBBC77CF42DC1E62D06227935379B4AA4D14FEA4F565DDF4CB4FA4D31579F9676, + 0x8E16604A4AFDF28246684D4996274781F6CB80ABBBA1414C1513EC988509DABF, + ) + ) + self.assertEqual( + c.exp(0xDC497D9EF6324912FD367840EE509A2032AEDB1C0A890D133B45F596FCCBD45D), + ( + 0x6097341C1BE388E83E7CA2DF47FAB86E2271FD942E5B7B2EB2409E49F742BC29, + 0xC81AA48BDB4CA6FA0EF18B9788AE25FE30857AA681B3942217F9FED151BAB7D0, + ), + ) + + def test_curve2(self): + c = CURVES["id-GostR3410-2001-CryptoPro-B-ParamSet"] + q_ind = ( + 0x3D715A874A4B17CB3B517893A9794A2B36C89D2FFC693F01EE4CC27E7F49E399, + 0x1C5A641FCF7CE7E87CDF8CEA38F3DB3096EACE2FAD158384B53953365F4FE7FE, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F18127067" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x6DC2AE26BC691FCA5A73D9C452790D15E34BA5404D92955B914C8D2662ABB985, + 0x3B02AAA9DD65AE30C335CED12F3154BBAC059F66B088306747453EDF6E5DB077, + ) + ) + self.assertEqual( + c.exp(0x499D72B90299CAB0DA1F8BE19D9122F622A13B32B730C46BD0664044F2144FAD), + ( + 0x61D6F916DB717222D74877F179F7EBEF7CD4D24D8C1F523C048E34A1DF30F8DD, + 0x3EC48863049CFCFE662904082E78503F4973A4E105E2F1B18C69A5E7FB209000, + ) + ) + self.assertEqual( + c.exp(0x0F69FF614957EF83668EDC2D7ED614BE76F7B253DB23C5CC9C52BF7DF8F4669D), + ( + 0x33BC6F7E9C0BA10CFB2B72546C327171295508EA97F8C8BA9F890F2478AB4D6C, + 0x75D57B396C396F492F057E9222CCC686437A2AAD464E452EF426FC8EEED1A4A6, + ), + ) + + def test_curve3(self): + c = CURVES["id-GostR3410-2001-CryptoPro-C-ParamSet"] + q_ind = ( + 0x1E36383E43BB6CFA2917167D71B7B5DD3D6D462B43D7C64282AE67DFBEC2559D, + 0x137478A9F721C73932EA06B45CF72E37EB78A63F29A542E563C614650C8B6399, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F18127067" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x945821DAF91E158B839939630655A3B21FF3E146D27041E86C05650EB3B46B59, + 0x3A0C2816AC97421FA0E879605F17F0C9C3EB734CFF196937F6284438D70BDC48, + ) + ) + self.assertEqual( + c.exp(0x3A54AC3F19AD9D0B1EAC8ACDCEA70E581F1DAC33D13FEAFD81E762378639C1A8), + ( + 0x96B7F09C94D297C257A7DA48364C0076E59E48D221CBA604AE111CA3933B446A, + 0x54E4953D86B77ECCEB578500931E822300F7E091F79592CA202A020D762C34A6, + ) + ) + self.assertEqual( + c.exp(0x448781782BF7C0E52A1DD9E6758FD3482D90D3CFCCF42232CF357E59A4D49FD4), + ( + 0x4B9C0AB55A938121F282F48A2CC4396EB16E7E0068B495B0C1DD4667786A3EB7, + 0x223460AA8E09383E9DF9844C5A0F2766484738E5B30128A171B69A77D9509B96, + ), + ) + + def test_curve4(self): + c = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + q_ind = ( + 0x2A17F8833A32795327478871B5C5E88AEFB91126C64B4B8327289BEA62559425D18198F133F400874328B220C74497CD240586CB249E158532CB8090776CD61C, + 0x728F0C4A73B48DA41CE928358FAD26B47A6E094E9362BAE82559F83CDDC4EC3A4676BD3707EDEAF4CD85E99695C64C241EDC622BE87DC0CF87F51F4367F723C5, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F181270671C6213E3930EFDDA26451792C6208122EE60D200520D695DFD9F5F0FD5ABA702" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x0C0AB53D0E0A9C607CAD758F558915A0A7DC5DC87B45E9A58FDDF30EC3385960283E030CD322D9E46B070637785FD49D2CD711F46807A24C40AF9A42C8E2D740, + 0xDF93A8012B86D3A3D4F8A4D487DA15FC739EB31B20B3B0E8C8C032AAF8072C6337CF7D5B404719E5B4407C41D9A3216A08CA69C271484E9ED72B8AAA52E28B8B, + ) + ) + self.assertEqual( + c.exp(0x3CE54325DB52FE798824AEAD11BB16FA766857D04A4AF7D468672F16D90E7396046A46F815693E85B1CE5464DA9270181F82333B0715057BBE8D61D400505F0E), + ( + 0xB93093EB0FCC463239B7DF276E09E592FCFC9B635504EA4531655D76A0A3078E2B4E51CFE2FA400CC5DE9FBE369DB204B3E8ED7EDD85EE5CCA654C1AED70E396, + 0x809770B8D910EA30BD2FA89736E91DC31815D2D9B31128077EEDC371E9F69466F497DC64DD5B1FADC587F860EE256109138C4A9CD96B628E65A8F590520FC882, + ) + ) + self.assertEqual( + c.exp(0xB5C286A79AA8E97EC0E19BC1959A1D15F12F8C97870BA9D68CC12811A56A3BB11440610825796A49D468CDC9C2D02D76598A27973D5960C5F50BCE28D8D345F4), + ( + 0x238B38644E440452A99FA6B93D9FD7DA0CB83C32D3C1E3CFE5DF5C3EB0F9DB91E588DAEDC849EA2FB867AE855A21B4077353C0794716A6480995113D8C20C7AF, + 0xB2273D5734C1897F8D15A7008B862938C8C74CA7E877423D95243EB7EBD02FD2C456CF9FC956F078A59AA86F19DD1075E5167E4ED35208718EA93161C530ED14, + ), + ) + + def test_curve5(self): + c = CURVES["id-tc26-gost-3410-12-512-paramSetB"] + q_ind = ( + 0x7E1FAE8285E035BEC244BEF2D0E5EBF436633CF50E55231DEA9C9CF21D4C8C33DF85D4305DE92971F0A4B4C07E00D87BDBC720EB66E49079285AAF12E0171149, + 0x2CC89998B875D4463805BA0D858A196592DB20AB161558FF2F4EF7A85725D20953967AE621AFDEAE89BB77C83A2528EF6FCE02F68BDA4679D7F2704947DBC408, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F181270671C6213E3930EFDDA26451792C6208122EE60D200520D695DFD9F5F0FD5ABA702" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x7D03E65B8050D1E12CBB601A17B9273B0E728F5021CD47C8A4DD822E4627BA5F9C696286A2CDDA9A065509866B4DEDEDC4A118409604AD549F87A60AFA621161, + 0x16037DAD45421EC50B00D50BDC6AC3B85348BC1D3A2F85DB27C3373580FEF87C2C743B7ED30F22BE22958044E716F93A61CA3213A361A2797A16A3AE62957377, + ) + ) + self.assertEqual( + c.exp(0x715E893FA639BF341296E0623E6D29DADF26B163C278767A7982A989462A3863FE12AEF8BD403D59C4DC4720570D4163DB0805C7C10C4E818F9CB785B04B9997), + ( + 0x10C479EA1C04D3C2C02B0576A9C42D96226FF033C1191436777F66916030D87D02FB93738ED7669D07619FFCE7C1F3C4DB5E5DF49E2186D6FA1E2EB5767602B9, + 0x039F6044191404E707F26D59D979136A831CCE43E1C5F0600D1DDF8F39D0CA3D52FBD943BF04DDCED1AA2CE8F5EBD7487ACDEF239C07D015084D796784F35436, + ) + ) + self.assertEqual( + c.exp(0x30FA8C2B4146C2DBBE82BED04D7378877E8C06753BD0A0FF71EBF2BEFE8DA8F3DC0836468E2CE7C5C961281B6505140F8407413F03C2CB1D201EA1286CE30E6D), + ( + 0x34C0149E7BB91AE377B02573FCC48AF7BFB7B16DEB8F9CE870F384688E3241A3A868588CC0EF4364CCA67D17E3260CD82485C202ADC76F895D5DF673B1788E67, + 0x608E944929BD643569ED5189DB871453F13333A1EAF82B2FE1BE8100E775F13DD9925BD317B63BFAF05024D4A738852332B64501195C1B2EF789E34F23DDAFC5, + ), + ) + + def test_curve6(self): + c = CURVES["id-tc26-gost-3410-2012-256-paramSetA"] + q_ind = ( + 0xB51ADF93A40AB15792164FAD3352F95B66369EB2A4EF5EFAE32829320363350E, + 0x74A358CC08593612F5955D249C96AFB7E8B0BB6D8BD2BBE491046650D822BE18, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F18127067" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0xDBF99827078956812FA48C6E695DF589DEF1D18A2D4D35A96D75BF6854237629, + 0x9FDDD48BFBC57BEE1DA0CFF282884F284D471B388893C48F5ECB02FC18D67589, + ) + ) + self.assertEqual( + c.exp(0x147B72F6684FB8FD1B418A899F7DBECAF5FCE60B13685BAA95328654A7F0707F), + ( + 0x33FBAC14EAE538275A769417829C431BD9FA622B6F02427EF55BD60EE6BC2888, + 0x22F2EBCF960A82E6CDB4042D3DDDA511B2FBA925383C2273D952EA2D406EAE46, + ) + ) + self.assertEqual( + c.exp(0x30D5CFADAA0E31B405E6734C03EC4C5DF0F02F4BA25C9A3B320EE6453567B4CB), + ( + 0x2B2D89FAB735433970564F2F28CFA1B57D640CB902BC6334A538F44155022CB2, + 0x10EF6A82EEF1E70F942AA81D6B4CE5DEC0DDB9447512962874870E6F2849A96F, + ), + ) + + def test_curve7(self): + c = CURVES["id-tc26-gost-3410-2012-512-paramSetC"] + q_ind = ( + 0x489C91784E02E98F19A803ABCA319917F37689E5A18965251CE2FF4E8D8B298F5BA7470F9E0E713487F96F4A8397B3D09A270C9D367EB5E0E6561ADEEB51581D, + 0x684EA885ACA64EAF1B3FEE36C0852A3BE3BD8011B0EF18E203FF87028D6EB5DB2C144A0DCC71276542BFD72CA2A43FA4F4939DA66D9A60793C704A8C94E16F18, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F181270671C6213E3930EFDDA26451792C6208122EE60D200520D695DFD9F5F0FD5ABA702" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x0185AE6271A81BB7F236A955F7CAA26FB63849813C0287D96C83A15AE6B6A86467AB13B6D88CE8CD7DC2E5B97FF5F28FAC2C108F2A3CF3DB5515C9E6D7D210E8, + 0xED0220F92EF771A71C64ECC77986DB7C03D37B3E2AB3E83F32CE5E074A762EC08253C9E2102B87532661275C4B1D16D2789CDABC58ACFDF7318DE70AB64F09B8, + ) + ) + self.assertEqual( + c.exp(0x332F930421D14CFE260042159F18E49FD5A54167E94108AD80B1DE60B13DE7999A34D611E63F3F870E5110247DF8EC7466E648ACF385E52CCB889ABF491EDFF0), + ( + 0x561655966D52952E805574F4281F1ED3A2D498932B00CBA9DECB42837F09835BFFBFE2D84D6B6B242FE7B57F92E1A6F2413E12DDD6383E4437E13D72693469AD, + 0xF6B18328B2715BD7F4178615273A36135BC0BF62F7D8BB9F080164AD36470AD03660F51806C64C6691BADEF30F793720F8E3FEAED631D6A54A4C372DCBF80E82, + ) + ) + self.assertEqual( + c.exp(0x38481771E7D054F96212686B613881880BD8A6C89DDBC656178F014D2C093432A033EE10415F13A160D44C2AD61E6E2E05A7F7EC286BCEA3EA4D4D53F8634FA2), + ( + 0xB7C5818687083433BC1AFF61CB5CA79E38232025E0C1F123B8651E62173CE6873F3E6FFE7281C2E45F4F524F66B0C263616ED08FD210AC4355CA3292B51D71C3, + 0x497F14205DBDC89BDDAF50520ED3B1429AD30777310186BE5E68070F016A44E0C766DB08E8AC23FBDFDE6D675AA4DF591EB18BA0D348DF7AA40973A2F1DCFA55, + ), + ) diff --git a/pygost-5.13/build/lib/pygost/test_gost3410_vko.py b/pygost-5.13/build/lib/pygost/test_gost3410_vko.py new file mode 100644 index 0000000..a8e298e --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_gost3410_vko.py @@ -0,0 +1,125 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from unittest import TestCase + +from pygost.gost3410 import CURVES +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import pub_unmarshal +from pygost.gost3410 import public_key +from pygost.gost3410_vko import kek_34102001 +from pygost.gost3410_vko import kek_34102012256 +from pygost.gost3410_vko import kek_34102012512 +from pygost.gost3410_vko import ukm_unmarshal +from pygost.utils import bytes2long +from pygost.utils import hexdec + + +class TestVKO34102001(TestCase): + def test_vector(self): + curve = CURVES["id-GostR3410-2001-TestParamSet"] + ukm = ukm_unmarshal(hexdec("5172be25f852a233")) + prv1 = prv_unmarshal(hexdec("1df129e43dab345b68f6a852f4162dc69f36b2f84717d08755cc5c44150bf928")) + prv2 = prv_unmarshal(hexdec("5b9356c6474f913f1e83885ea0edd5df1a43fd9d799d219093241157ac9ed473")) + kek = hexdec("ee4618a0dbb10cb31777b4b86a53d9e7ef6cb3e400101410f0c0f2af46c494a6") + pub1 = public_key(curve, prv1) + pub2 = public_key(curve, prv2) + self.assertSequenceEqual(kek_34102001(curve, prv1, pub2, ukm), kek) + self.assertSequenceEqual(kek_34102001(curve, prv2, pub1, ukm), kek) + + def test_sequence(self): + curve = CURVES["id-GostR3410-2001-TestParamSet"] + for _ in range(10): + ukm = ukm_unmarshal(urandom(8)) + prv1 = bytes2long(urandom(32)) + prv2 = bytes2long(urandom(32)) + pub1 = public_key(curve, prv1) + pub2 = public_key(curve, prv2) + kek1 = kek_34102001(curve, prv1, pub2, ukm) + kek2 = kek_34102001(curve, prv2, pub1, ukm) + self.assertSequenceEqual(kek1, kek2) + kek1 = kek_34102001(curve, prv1, pub1, ukm) + kek2 = kek_34102001(curve, prv2, pub2, ukm) + self.assertNotEqual(kek1, kek2) + + +class TestVKO34102012256(TestCase): + """RFC 7836 + """ + def test_vector(self): + curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + ukm = ukm_unmarshal(hexdec("1d80603c8544c727")) + prvA = prv_unmarshal(hexdec("c990ecd972fce84ec4db022778f50fcac726f46708384b8d458304962d7147f8c2db41cef22c90b102f2968404f9b9be6d47c79692d81826b32b8daca43cb667")) + pubA = pub_unmarshal(hexdec("aab0eda4abff21208d18799fb9a8556654ba783070eba10cb9abb253ec56dcf5d3ccba6192e464e6e5bcb6dea137792f2431f6c897eb1b3c0cc14327b1adc0a7914613a3074e363aedb204d38d3563971bd8758e878c9db11403721b48002d38461f92472d40ea92f9958c0ffa4c93756401b97f89fdbe0b5e46e4a4631cdb5a")) + prvB = prv_unmarshal(hexdec("48c859f7b6f11585887cc05ec6ef1390cfea739b1a18c0d4662293ef63b79e3b8014070b44918590b4b996acfea4edfbbbcccc8c06edd8bf5bda92a51392d0db")) + pubB = pub_unmarshal(hexdec("192fe183b9713a077253c72c8735de2ea42a3dbc66ea317838b65fa32523cd5efca974eda7c863f4954d1147f1f2b25c395fce1c129175e876d132e94ed5a65104883b414c9b592ec4dc84826f07d0b6d9006dda176ce48c391e3f97d102e03bb598bf132a228a45f7201aba08fc524a2d77e43a362ab022ad4028f75bde3b79")) + vko = hexdec("c9a9a77320e2cc559ed72dce6f47e2192ccea95fa648670582c054c0ef36c221") + self.assertSequenceEqual(kek_34102012256(curve, prvA, pubB, ukm), vko) + self.assertSequenceEqual(kek_34102012256(curve, prvB, pubA, ukm), vko) + + def test_sequence(self): + curve = CURVES["id-tc26-gost-3410-2012-256-paramSetA"] + for _ in range(10): + ukm = ukm_unmarshal(urandom(8)) + prv1 = bytes2long(urandom(32)) + prv2 = bytes2long(urandom(32)) + pub1 = public_key(curve, prv1) + pub2 = public_key(curve, prv2) + kek1 = kek_34102012256(curve, prv1, pub2, ukm) + kek2 = kek_34102012256(curve, prv2, pub1, ukm) + self.assertSequenceEqual(kek1, kek2) + kek1 = kek_34102012256(curve, prv1, pub1, ukm) + kek2 = kek_34102012256(curve, prv2, pub2, ukm) + self.assertNotEqual(kek1, kek2) + + def test_pub_is_not_on_curve(self): + with self.assertRaises(ValueError): + kek_34102012256( + CURVES["id-tc26-gost-3410-2012-256-paramSetA"], + bytes2long(urandom(32)), + pub_unmarshal(urandom(64)), + ) + + +class TestVKO34102012512(TestCase): + """RFC 7836 + """ + def test_vector(self): + curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + ukm = ukm_unmarshal(hexdec("1d80603c8544c727")) + prvA = prv_unmarshal(hexdec("c990ecd972fce84ec4db022778f50fcac726f46708384b8d458304962d7147f8c2db41cef22c90b102f2968404f9b9be6d47c79692d81826b32b8daca43cb667")) + pubA = pub_unmarshal(hexdec("aab0eda4abff21208d18799fb9a8556654ba783070eba10cb9abb253ec56dcf5d3ccba6192e464e6e5bcb6dea137792f2431f6c897eb1b3c0cc14327b1adc0a7914613a3074e363aedb204d38d3563971bd8758e878c9db11403721b48002d38461f92472d40ea92f9958c0ffa4c93756401b97f89fdbe0b5e46e4a4631cdb5a")) + prvB = prv_unmarshal(hexdec("48c859f7b6f11585887cc05ec6ef1390cfea739b1a18c0d4662293ef63b79e3b8014070b44918590b4b996acfea4edfbbbcccc8c06edd8bf5bda92a51392d0db")) + pubB = pub_unmarshal(hexdec("192fe183b9713a077253c72c8735de2ea42a3dbc66ea317838b65fa32523cd5efca974eda7c863f4954d1147f1f2b25c395fce1c129175e876d132e94ed5a65104883b414c9b592ec4dc84826f07d0b6d9006dda176ce48c391e3f97d102e03bb598bf132a228a45f7201aba08fc524a2d77e43a362ab022ad4028f75bde3b79")) + vko = hexdec("79f002a96940ce7bde3259a52e015297adaad84597a0d205b50e3e1719f97bfa7ee1d2661fa9979a5aa235b558a7e6d9f88f982dd63fc35a8ec0dd5e242d3bdf") + self.assertSequenceEqual(kek_34102012512(curve, prvA, pubB, ukm), vko) + self.assertSequenceEqual(kek_34102012512(curve, prvB, pubA, ukm), vko) + + def test_sequence(self): + curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + for _ in range(10): + ukm = ukm_unmarshal(urandom(8)) + prv1 = bytes2long(urandom(32)) + prv2 = bytes2long(urandom(32)) + pub1 = public_key(curve, prv1) + pub2 = public_key(curve, prv2) + kek1 = kek_34102012512(curve, prv1, pub2, ukm) + kek2 = kek_34102012512(curve, prv2, pub1, ukm) + self.assertSequenceEqual(kek1, kek2) + kek1 = kek_34102012512(curve, prv1, pub1, ukm) + kek2 = kek_34102012512(curve, prv2, pub2, ukm) + self.assertNotEqual(kek1, kek2) diff --git a/pygost-5.13/build/lib/pygost/test_gost34112012.py b/pygost-5.13/build/lib/pygost/test_gost34112012.py new file mode 100644 index 0000000..c7c2df9 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_gost34112012.py @@ -0,0 +1,159 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from random import randint +from unittest import skip +from unittest import TestCase +import hmac + +from pygost import gost34112012256 +from pygost import gost34112012512 +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.gost34112012512 import pbkdf2 +from pygost.utils import hexdec +from pygost.utils import hexenc + + +class TestCopy(TestCase): + def runTest(self): + m = GOST34112012256() + c = m.copy() + m.update(b"foobar") + c.update(b"foo") + c.update(b"bar") + self.assertSequenceEqual(m.digest(), c.digest()) + + +class TestSymmetric(TestCase): + def runTest(self): + chunks = [] + for _ in range(randint(1, 10)): + chunks.append(urandom(randint(20, 80))) + m = GOST34112012256() + for chunk in chunks: + m.update(chunk) + self.assertSequenceEqual( + m.hexdigest(), + GOST34112012256(b"".join(chunks)).hexdigest(), + ) + + +class TestHMAC(TestCase): + """RFC 7836 + """ + def test_256(self): + for digestmod in (GOST34112012256, gost34112012256): + self.assertSequenceEqual( + hmac.new( + key=hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + msg=hexdec("0126bdb87800af214341456563780100"), + digestmod=digestmod, + ).hexdigest(), + "a1aa5f7de402d7b3d323f2991c8d4534013137010a83754fd0af6d7cd4922ed9", + ) + + def test_512(self): + for digestmod in (GOST34112012512, gost34112012512): + self.assertSequenceEqual( + hmac.new( + key=hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + msg=hexdec("0126bdb87800af214341456563780100"), + digestmod=digestmod, + ).hexdigest(), + "a59bab22ecae19c65fbde6e5f4e9f5d8549d31f037f9df9b905500e171923a773d5f1530f2ed7e964cb2eedc29e9ad2f3afe93b2814f79f5000ffc0366c251e6", + ) + + +class TestVectors(TestCase): + def test_m1(self): + m = hexdec("323130393837363534333231303938373635343332313039383736353433323130393837363534333231303938373635343332313039383736353433323130")[::-1] + self.assertSequenceEqual( + GOST34112012512(m).digest(), + hexdec("486f64c1917879417fef082b3381a4e211c324f074654c38823a7b76f830ad00fa1fbae42b1285c0352f227524bc9ab16254288dd6863dccd5b9f54a1ad0541b")[::-1] + ) + self.assertSequenceEqual( + GOST34112012256(m).digest(), + hexdec("00557be5e584fd52a449b16b0251d05d27f94ab76cbaa6da890b59d8ef1e159d")[::-1] + ) + + def test_m2(self): + m = u"Се ветри, Стрибожи внуци, веютъ с моря стрелами на храбрыя плъкы Игоревы".encode("cp1251") + self.assertSequenceEqual(m, hexdec("fbe2e5f0eee3c820fbeafaebef20fffbf0e1e0f0f520e0ed20e8ece0ebe5f0f2f120fff0eeec20f120faf2fee5e2202ce8f6f3ede220e8e6eee1e8f0f2d1202ce8f0f2e5e220e5d1")[::-1]) + self.assertSequenceEqual( + GOST34112012512(m).digest(), + hexdec("28fbc9bada033b1460642bdcddb90c3fb3e56c497ccd0f62b8a2ad4935e85f037613966de4ee00531ae60f3b5a47f8dae06915d5f2f194996fcabf2622e6881e")[::-1] + ) + self.assertSequenceEqual( + GOST34112012256(m).digest(), + hexdec("508f7e553c06501d749a66fc28c6cac0b005746d97537fa85d9e40904efed29d")[::-1] + ) + + def test_habr144(self): + """Test vector from https://habr.com/ru/post/452200/ + """ + m = hexdec("d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + self.assertSequenceEqual( + GOST34112012256(m).hexdigest(), + "c766085540caaa8953bfcf7a1ba220619cee50d65dc242f82f23ba4b180b18e0", + ) + + +class TestPBKDF2(TestCase): + """http://tc26.ru/.../R_50.1.111-2016.pdf + """ + def test_1(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 1, 64)), + "64770af7f748c3b1c9ac831dbcfd85c26111b30a8a657ddc3056b80ca73e040d2854fd36811f6d825cc4ab66ec0a68a490a9e5cf5156b3a2b7eecddbf9a16b47", + ) + + def test_2(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 2, 64)), + "5a585bafdfbb6e8830d6d68aa3b43ac00d2e4aebce01c9b31c2caed56f0236d4d34b2b8fbd2c4e89d54d46f50e47d45bbac301571743119e8d3c42ba66d348de", + ) + + def test_3(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 4096, 64)), + "e52deb9a2d2aaff4e2ac9d47a41f34c20376591c67807f0477e32549dc341bc7867c09841b6d58e29d0347c996301d55df0d34e47cf68f4e3c2cdaf1d9ab86c3", + ) + + @skip("it takes too long") + def test_4(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 1677216, 64)), + "49e4843bba76e300afe24c4d23dc7392def12f2c0e244172367cd70a8982ac361adb601c7e2a314e8cb7b1e9df840e36ab5615be5d742b6cf203fb55fdc48071", + ) + + def test_5(self): + self.assertSequenceEqual( + hexenc(pbkdf2( + b"passwordPASSWORDpassword", + b"saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + 100, + )), + "b2d8f1245fc4d29274802057e4b54e0a0753aa22fc53760b301cf008679e58fe4bee9addcae99ba2b0b20f431a9c5e50f395c89387d0945aedeca6eb4015dfc2bd2421ee9bb71183ba882ceebfef259f33f9e27dc6178cb89dc37428cf9cc52a2baa2d3a", + ) + + def test_6(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"pass\x00word", b"sa\x00lt", 4096, 64)), + "50df062885b69801a3c10248eb0a27ab6e522ffeb20c991c660f001475d73a4e167f782c18e97e92976d9c1d970831ea78ccb879f67068cdac1910740844e830", + ) diff --git a/pygost-5.13/build/lib/pygost/test_gost341194.py b/pygost-5.13/build/lib/pygost/test_gost341194.py new file mode 100644 index 0000000..4ffa6d6 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_gost341194.py @@ -0,0 +1,200 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from unittest import skip +from unittest import TestCase +import hmac + +from pygost import gost341194 +from pygost.gost341194 import GOST341194 +from pygost.gost341194 import pbkdf2 +from pygost.utils import hexenc + + +class TestCopy(TestCase): + def runTest(self): + m = GOST341194() + c = m.copy() + m.update(b"foobar") + c.update(b"foo") + c.update(b"bar") + self.assertSequenceEqual(m.digest(), c.digest()) + + +class TestHMACPEP247(TestCase): + def runTest(self): + h = hmac.new(b"foo", digestmod=gost341194) + h.update(b"foobar") + h.digest() + + +class TestVectors(TestCase): + def test_empty(self): + self.assertSequenceEqual( + GOST341194(b"", "id-GostR3411-94-TestParamSet").hexdigest(), + "ce85b99cc46752fffee35cab9a7b0278abb4c2d2055cff685af4912c49490f8d", + ) + + def test_a(self): + self.assertSequenceEqual( + GOST341194(b"a", "id-GostR3411-94-TestParamSet").hexdigest(), + "d42c539e367c66e9c88a801f6649349c21871b4344c6a573f849fdce62f314dd", + ) + + def test_abc(self): + self.assertSequenceEqual( + GOST341194(b"abc", "id-GostR3411-94-TestParamSet").hexdigest(), + "f3134348c44fb1b2a277729e2285ebb5cb5e0f29c975bc753b70497c06a4d51d", + ) + + def test_message_digest(self): + self.assertSequenceEqual( + GOST341194(b"message digest", "id-GostR3411-94-TestParamSet").hexdigest(), + "ad4434ecb18f2c99b60cbe59ec3d2469582b65273f48de72db2fde16a4889a4d", + ) + + def test_Us(self): + self.assertSequenceEqual( + GOST341194(128 * b"U", "id-GostR3411-94-TestParamSet").hexdigest(), + "53a3a3ed25180cef0c1d85a074273e551c25660a87062a52d926a9e8fe5733a4", + ) + + def test_dog(self): + self.assertSequenceEqual( + GOST341194(b"The quick brown fox jumps over the lazy dog", "id-GostR3411-94-TestParamSet",).hexdigest(), + "77b7fa410c9ac58a25f49bca7d0468c9296529315eaca76bd1a10f376d1f4294", + ) + + def test_cog(self): + self.assertSequenceEqual( + GOST341194(b"The quick brown fox jumps over the lazy cog", "id-GostR3411-94-TestParamSet",).hexdigest(), + "a3ebc4daaab78b0be131dab5737a7f67e602670d543521319150d2e14eeec445", + ) + + def test_rfc32(self): + self.assertSequenceEqual( + GOST341194(b"This is message, length=32 bytes", "id-GostR3411-94-TestParamSet",).hexdigest(), + "b1c466d37519b82e8319819ff32595e047a28cb6f83eff1c6916a815a637fffa", + ) + + def test_rfc50(self): + self.assertSequenceEqual( + GOST341194(b"Suppose the original message has length = 50 bytes", "id-GostR3411-94-TestParamSet",).hexdigest(), + "471aba57a60a770d3a76130635c1fbea4ef14de51f78b4ae57dd893b62f55208", + ) + + +class TestVectorsCryptoPro(TestCase): + """CryptoPro S-box test vectors + """ + def test_empty(self): + self.assertSequenceEqual( + GOST341194(b"", "id-GostR3411-94-CryptoProParamSet").hexdigest(), + "981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0", + ) + + def test_a(self): + self.assertSequenceEqual( + GOST341194(b"a", "id-GostR3411-94-CryptoProParamSet").hexdigest(), + "e74c52dd282183bf37af0079c9f78055715a103f17e3133ceff1aacf2f403011", + ) + + def test_abc(self): + self.assertSequenceEqual( + GOST341194(b"abc", "id-GostR3411-94-CryptoProParamSet").hexdigest(), + "b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c", + ) + + def test_message_digest(self): + self.assertSequenceEqual( + GOST341194(b"message digest", "id-GostR3411-94-CryptoProParamSet",).hexdigest(), + "bc6041dd2aa401ebfa6e9886734174febdb4729aa972d60f549ac39b29721ba0", + ) + + def test_dog(self): + self.assertSequenceEqual( + GOST341194(b"The quick brown fox jumps over the lazy dog", "id-GostR3411-94-CryptoProParamSet",).hexdigest(), + "9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76", + ) + + def test_32(self): + self.assertSequenceEqual( + GOST341194(b"This is message, length=32 bytes", "id-GostR3411-94-CryptoProParamSet",).hexdigest(), + "2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb", + ) + + def test_50(self): + self.assertSequenceEqual( + GOST341194(b"Suppose the original message has length = 50 bytes", "id-GostR3411-94-CryptoProParamSet",).hexdigest(), + "c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011", + ) + + def test_Us(self): + self.assertSequenceEqual( + GOST341194(128 * b"U", "id-GostR3411-94-CryptoProParamSet").hexdigest(), + "1c4ac7614691bbf427fa2316216be8f10d92edfd37cd1027514c1008f649c4e8", + ) + + +class TestPBKDF2(TestCase): + """http://tc26.ru/methods/containers_v1/Addition_to_PKCS5_v1_0.pdf test vectors + """ + def test_1(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 1, 32)), + "7314e7c04fb2e662c543674253f68bd0b73445d07f241bed872882da21662d58", + ) + + def test_2(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 2, 32)), + "990dfa2bd965639ba48b07b792775df79f2db34fef25f274378872fed7ed1bb3", + ) + + def test_3(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 4096, 32)), + "1f1829a94bdff5be10d0aeb36af498e7a97467f3b31116a5a7c1afff9deadafe", + ) + + @skip("it takes too long") + def test_4(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 16777216, 32)), + "a57ae5a6088396d120850c5c09de0a525100938a59b1b5c3f7810910d05fcd97", + ) + + def test_5(self): + self.assertSequenceEqual( + hexenc(pbkdf2( + b"passwordPASSWORDpassword", + b"saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + 40, + )), + "788358c69cb2dbe251a7bb17d5f4241f265a792a35becde8d56f326b49c85047b7638acb4764b1fd", + ) + + def test_6(self): + self.assertSequenceEqual( + hexenc(pbkdf2( + b"pass\x00word", + b"sa\x00lt", + 4096, + 20, + )), + "43e06c5590b08c0225242373127edf9c8e9c3291", + ) diff --git a/pygost-5.13/build/lib/pygost/test_gost3412.py b/pygost-5.13/build/lib/pygost/test_gost3412.py new file mode 100644 index 0000000..5dbb200 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_gost3412.py @@ -0,0 +1,137 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from unittest import TestCase + +from pygost.gost3412 import C +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3412 import L +from pygost.gost3412 import PI +from pygost.utils import hexdec + + +def S(blk): + return bytearray(PI[v] for v in blk) + + +def R(blk): + return L(blk, rounds=1) + + +class STest(TestCase): + def test_vec1(self): + blk = bytearray(hexdec("ffeeddccbbaa99881122334455667700")) + self.assertSequenceEqual(S(blk), hexdec("b66cd8887d38e8d77765aeea0c9a7efc")) + + def test_vec2(self): + blk = bytearray(hexdec("b66cd8887d38e8d77765aeea0c9a7efc")) + self.assertSequenceEqual(S(blk), hexdec("559d8dd7bd06cbfe7e7b262523280d39")) + + def test_vec3(self): + blk = bytearray(hexdec("559d8dd7bd06cbfe7e7b262523280d39")) + self.assertSequenceEqual(S(blk), hexdec("0c3322fed531e4630d80ef5c5a81c50b")) + + def test_vec4(self): + blk = bytearray(hexdec("0c3322fed531e4630d80ef5c5a81c50b")) + self.assertSequenceEqual(S(blk), hexdec("23ae65633f842d29c5df529c13f5acda")) + + +class RTest(TestCase): + def test_vec1(self): + blk = bytearray(hexdec("00000000000000000000000000000100")) + self.assertSequenceEqual(R(blk), hexdec("94000000000000000000000000000001")) + + def test_vec2(self): + blk = bytearray(hexdec("94000000000000000000000000000001")) + self.assertSequenceEqual(R(blk), hexdec("a5940000000000000000000000000000")) + + def test_vec3(self): + blk = bytearray(hexdec("a5940000000000000000000000000000")) + self.assertSequenceEqual(R(blk), hexdec("64a59400000000000000000000000000")) + + def test_vec4(self): + blk = bytearray(hexdec("64a59400000000000000000000000000")) + self.assertSequenceEqual(R(blk), hexdec("0d64a594000000000000000000000000")) + + +class LTest(TestCase): + def test_vec1(self): + blk = bytearray(hexdec("64a59400000000000000000000000000")) + self.assertSequenceEqual(L(blk), hexdec("d456584dd0e3e84cc3166e4b7fa2890d")) + + def test_vec2(self): + blk = bytearray(hexdec("d456584dd0e3e84cc3166e4b7fa2890d")) + self.assertSequenceEqual(L(blk), hexdec("79d26221b87b584cd42fbc4ffea5de9a")) + + def test_vec3(self): + blk = bytearray(hexdec("79d26221b87b584cd42fbc4ffea5de9a")) + self.assertSequenceEqual(L(blk), hexdec("0e93691a0cfc60408b7b68f66b513c13")) + + def test_vec4(self): + blk = bytearray(hexdec("0e93691a0cfc60408b7b68f66b513c13")) + self.assertSequenceEqual(L(blk), hexdec("e6a8094fee0aa204fd97bcb0b44b8580")) + + +class KuznechikTest(TestCase): + key = hexdec("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef") + plaintext = hexdec("1122334455667700ffeeddccbbaa9988") + ciphertext = hexdec("7f679d90bebc24305a468d42b9d4edcd") + + def test_c(self): + self.assertSequenceEqual(C[0], hexdec("6ea276726c487ab85d27bd10dd849401")) + self.assertSequenceEqual(C[1], hexdec("dc87ece4d890f4b3ba4eb92079cbeb02")) + self.assertSequenceEqual(C[2], hexdec("b2259a96b4d88e0be7690430a44f7f03")) + self.assertSequenceEqual(C[3], hexdec("7bcd1b0b73e32ba5b79cb140f2551504")) + self.assertSequenceEqual(C[4], hexdec("156f6d791fab511deabb0c502fd18105")) + self.assertSequenceEqual(C[5], hexdec("a74af7efab73df160dd208608b9efe06")) + self.assertSequenceEqual(C[6], hexdec("c9e8819dc73ba5ae50f5b570561a6a07")) + self.assertSequenceEqual(C[7], hexdec("f6593616e6055689adfba18027aa2a08")) + + def test_roundkeys(self): + ciph = GOST3412Kuznechik(self.key) + self.assertSequenceEqual(ciph.ks[0], hexdec("8899aabbccddeeff0011223344556677")) + self.assertSequenceEqual(ciph.ks[1], hexdec("fedcba98765432100123456789abcdef")) + self.assertSequenceEqual(ciph.ks[2], hexdec("db31485315694343228d6aef8cc78c44")) + self.assertSequenceEqual(ciph.ks[3], hexdec("3d4553d8e9cfec6815ebadc40a9ffd04")) + self.assertSequenceEqual(ciph.ks[4], hexdec("57646468c44a5e28d3e59246f429f1ac")) + self.assertSequenceEqual(ciph.ks[5], hexdec("bd079435165c6432b532e82834da581b")) + self.assertSequenceEqual(ciph.ks[6], hexdec("51e640757e8745de705727265a0098b1")) + self.assertSequenceEqual(ciph.ks[7], hexdec("5a7925017b9fdd3ed72a91a22286f984")) + self.assertSequenceEqual(ciph.ks[8], hexdec("bb44e25378c73123a5f32f73cdb6e517")) + self.assertSequenceEqual(ciph.ks[9], hexdec("72e9dd7416bcf45b755dbaa88e4a4043")) + + def test_encrypt(self): + ciph = GOST3412Kuznechik(self.key) + self.assertSequenceEqual(ciph.encrypt(self.plaintext), self.ciphertext) + + def test_decrypt(self): + ciph = GOST3412Kuznechik(self.key) + self.assertSequenceEqual(ciph.decrypt(self.ciphertext), self.plaintext) + + +class MagmaTest(TestCase): + key = hexdec("ffeeddccbbaa99887766554433221100f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") + plaintext = hexdec("fedcba9876543210") + ciphertext = hexdec("4ee901e5c2d8ca3d") + + def test_encrypt(self): + ciph = GOST3412Magma(self.key) + self.assertSequenceEqual(ciph.encrypt(self.plaintext), self.ciphertext) + + def test_decrypt(self): + ciph = GOST3412Magma(self.key) + self.assertSequenceEqual(ciph.decrypt(self.ciphertext), self.plaintext) diff --git a/pygost-5.13/build/lib/pygost/test_gost3413.py b/pygost-5.13/build/lib/pygost/test_gost3413.py new file mode 100644 index 0000000..0098513 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_gost3413.py @@ -0,0 +1,766 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from random import randint +from unittest import TestCase + +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3413 import _mac_ks +from pygost.gost3413 import acpkm +from pygost.gost3413 import acpkm_master +from pygost.gost3413 import cbc_decrypt +from pygost.gost3413 import cbc_encrypt +from pygost.gost3413 import cfb_decrypt +from pygost.gost3413 import cfb_encrypt +from pygost.gost3413 import ctr +from pygost.gost3413 import ctr_acpkm +from pygost.gost3413 import ecb_decrypt +from pygost.gost3413 import ecb_encrypt +from pygost.gost3413 import KEYSIZE +from pygost.gost3413 import mac +from pygost.gost3413 import mac_acpkm_master +from pygost.gost3413 import ofb +from pygost.gost3413 import pad2 +from pygost.gost3413 import pad_iso10126 +from pygost.gost3413 import unpad2 +from pygost.gost3413 import unpad_iso10126 +from pygost.utils import hexdec +from pygost.utils import hexenc +from pygost.utils import strxor + + +class Pad2Test(TestCase): + def test_symmetric(self): + for _ in range(100): + for blocksize in (GOST3412Magma.blocksize, GOST3412Kuznechik.blocksize): + data = urandom(randint(0, blocksize * 3)) + self.assertSequenceEqual( + unpad2(pad2(data, blocksize), blocksize), + data, + ) + + +class GOST3412KuznechikModesTest(TestCase): + key = hexdec("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef") + ciph = GOST3412Kuznechik(key) + plaintext = "" + plaintext += "1122334455667700ffeeddccbbaa9988" + plaintext += "00112233445566778899aabbcceeff0a" + plaintext += "112233445566778899aabbcceeff0a00" + plaintext += "2233445566778899aabbcceeff0a0011" + iv = hexdec("1234567890abcef0a1b2c3d4e5f0011223344556677889901213141516171819") + + def test_ecb_vectors(self): + ciphtext = "" + ciphtext += "7f679d90bebc24305a468d42b9d4edcd" + ciphtext += "b429912c6e0032f9285452d76718d08b" + ciphtext += "f0ca33549d247ceef3f5a5313bd4b157" + ciphtext += "d0b09ccde830b9eb3a02c4c5aa8ada98" + self.assertSequenceEqual( + hexenc(ecb_encrypt( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ecb_decrypt( + self.ciph.decrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + )), + self.plaintext, + ) + + def test_ecb_symmetric(self): + for _ in range(100): + pt = pad2(urandom(randint(0, 16 * 2)), GOST3412Kuznechik.blocksize) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = ecb_encrypt(ciph.encrypt, GOST3412Kuznechik.blocksize, pt) + self.assertSequenceEqual(ecb_decrypt( + ciph.decrypt, + GOST3412Kuznechik.blocksize, + ct, + ), pt) + + def test_ctr_vectors(self): + ciphtext = "" + ciphtext += "f195d8bec10ed1dbd57b5fa240bda1b8" + ciphtext += "85eee733f6a13e5df33ce4b33c45dee4" + ciphtext += "a5eae88be6356ed3d5e877f13564a3a5" + ciphtext += "cb91fab1f20cbab6d1c6d15820bdba73" + iv = self.iv[:GOST3412Kuznechik.blocksize // 2] + self.assertSequenceEqual( + hexenc(ctr( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ctr( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + iv, + )), + self.plaintext, + ) + + def test_ctr_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Kuznechik.blocksize // 2) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = ctr(ciph.encrypt, GOST3412Kuznechik.blocksize, pt, iv) + self.assertSequenceEqual(ctr( + ciph.encrypt, + GOST3412Kuznechik.blocksize, + ct, + iv, + ), pt) + + def test_ofb_vectors(self): + ciphtext = "" + ciphtext += "81800a59b1842b24ff1f795e897abd95" + ciphtext += "ed5b47a7048cfab48fb521369d9326bf" + ciphtext += "66a257ac3ca0b8b1c80fe7fc10288a13" + ciphtext += "203ebbc066138660a0292243f6903150" + self.assertSequenceEqual( + hexenc(ofb( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + self.iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ofb( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + self.iv, + )), + self.plaintext, + ) + + def test_ofb_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Kuznechik.blocksize * 2) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = ofb(ciph.encrypt, GOST3412Kuznechik.blocksize, pt, iv) + self.assertSequenceEqual(ofb( + ciph.encrypt, + GOST3412Kuznechik.blocksize, + ct, + iv, + ), pt) + + def test_ofb_manual(self): + iv = [urandom(GOST3412Kuznechik.blocksize) for _ in range(randint(2, 10))] + pt = [ + urandom(GOST3412Kuznechik.blocksize) + for _ in range(len(iv), len(iv) + randint(1, 10)) + ] + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + r = [ciph.encrypt(i) for i in iv] + for i in range(len(pt) - len(iv)): + r.append(ciph.encrypt(r[i])) + ct = [strxor(g, r) for g, r in zip(pt, r)] + self.assertSequenceEqual( + ofb(ciph.encrypt, GOST3412Kuznechik.blocksize, b"".join(pt), b"".join(iv)), + b"".join(ct), + ) + + def test_cbc_vectors(self): + ciphtext = "" + ciphtext += "689972d4a085fa4d90e52e3d6d7dcc27" + ciphtext += "2826e661b478eca6af1e8e448d5ea5ac" + ciphtext += "fe7babf1e91999e85640e8b0f49d90d0" + ciphtext += "167688065a895c631a2d9a1560b63970" + self.assertSequenceEqual( + hexenc(cbc_encrypt( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + self.iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(cbc_decrypt( + self.ciph.decrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + self.iv, + )), + self.plaintext, + ) + + def test_cbc_symmetric(self): + for _ in range(100): + pt = pad2(urandom(randint(0, 16 * 2)), GOST3412Kuznechik.blocksize) + iv = urandom(GOST3412Kuznechik.blocksize * 2) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = cbc_encrypt(ciph.encrypt, GOST3412Kuznechik.blocksize, pt, iv) + self.assertSequenceEqual(cbc_decrypt( + ciph.decrypt, + GOST3412Kuznechik.blocksize, + ct, + iv, + ), pt) + + def test_cfb_vectors(self): + ciphtext = "" + ciphtext += "81800a59b1842b24ff1f795e897abd95" + ciphtext += "ed5b47a7048cfab48fb521369d9326bf" + ciphtext += "79f2a8eb5cc68d38842d264e97a238b5" + ciphtext += "4ffebecd4e922de6c75bd9dd44fbf4d1" + self.assertSequenceEqual( + hexenc(cfb_encrypt( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + self.iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(cfb_decrypt( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + self.iv, + )), + self.plaintext, + ) + + def test_cfb_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Kuznechik.blocksize * 2) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = cfb_encrypt(ciph.encrypt, GOST3412Kuznechik.blocksize, pt, iv) + self.assertSequenceEqual(cfb_decrypt( + ciph.encrypt, + GOST3412Kuznechik.blocksize, + ct, + iv, + ), pt) + + def test_mac_vectors(self): + k1, k2 = _mac_ks(self.ciph.encrypt, GOST3412Kuznechik.blocksize) + self.assertSequenceEqual(hexenc(k1), "297d82bc4d39e3ca0de0573298151dc7") + self.assertSequenceEqual(hexenc(k2), "52fb05789a73c7941bc0ae65302a3b8e") + self.assertSequenceEqual( + hexenc(mac( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + )[:8]), + "336f4d296059fbe3", + ) + + def test_mac_applies(self): + for _ in range(100): + data = urandom(randint(0, 16 * 2)) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + mac(ciph.encrypt, GOST3412Kuznechik.blocksize, data) + + +class GOST3412MagmaModesTest(TestCase): + key = hexdec("ffeeddccbbaa99887766554433221100f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") + ciph = GOST3412Magma(key) + plaintext = "" + plaintext += "92def06b3c130a59" + plaintext += "db54c704f8189d20" + plaintext += "4a98fb2e67a8024c" + plaintext += "8912409b17b57e41" + iv = hexdec("1234567890abcdef234567890abcdef134567890abcdef12") + + def test_ecb_vectors(self): + ciphtext = "" + ciphtext += "2b073f0494f372a0" + ciphtext += "de70e715d3556e48" + ciphtext += "11d8d9e9eacfbc1e" + ciphtext += "7c68260996c67efb" + self.assertSequenceEqual( + hexenc(ecb_encrypt( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ecb_decrypt( + self.ciph.decrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + )), + self.plaintext, + ) + + def test_ecb_symmetric(self): + for _ in range(100): + pt = pad2(urandom(randint(0, 16 * 2)), 16) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = ecb_encrypt(ciph.encrypt, GOST3412Magma.blocksize, pt) + self.assertSequenceEqual(ecb_decrypt( + ciph.decrypt, + GOST3412Magma.blocksize, + ct, + ), pt) + + def test_ctr_vectors(self): + ciphtext = "" + ciphtext += "4e98110c97b7b93c" + ciphtext += "3e250d93d6e85d69" + ciphtext += "136d868807b2dbef" + ciphtext += "568eb680ab52a12d" + iv = self.iv[:4] + self.assertSequenceEqual( + hexenc(ctr( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ctr( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + iv, + )), + self.plaintext, + ) + + def test_ctr_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Magma.blocksize // 2) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = ctr(ciph.encrypt, GOST3412Magma.blocksize, pt, iv) + self.assertSequenceEqual(ctr( + ciph.encrypt, + GOST3412Magma.blocksize, + ct, + iv, + ), pt) + + def test_ofb_vectors(self): + iv = self.iv[:16] + ciphtext = "" + ciphtext += "db37e0e266903c83" + ciphtext += "0d46644c1f9a089c" + ciphtext += "a0f83062430e327e" + ciphtext += "c824efb8bd4fdb05" + self.assertSequenceEqual( + hexenc(ofb( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ofb( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + iv, + )), + self.plaintext, + ) + + def test_ofb_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Magma.blocksize * 2) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = ofb(ciph.encrypt, GOST3412Magma.blocksize, pt, iv) + self.assertSequenceEqual(ofb( + ciph.encrypt, + GOST3412Magma.blocksize, + ct, + iv, + ), pt) + + def test_cbc_vectors(self): + ciphtext = "" + ciphtext += "96d1b05eea683919" + ciphtext += "aff76129abb937b9" + ciphtext += "5058b4a1c4bc0019" + ciphtext += "20b78b1a7cd7e667" + self.assertSequenceEqual( + hexenc(cbc_encrypt( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + self.iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(cbc_decrypt( + self.ciph.decrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + self.iv, + )), + self.plaintext, + ) + + def test_cbc_symmetric(self): + for _ in range(100): + pt = pad2(urandom(randint(0, 16 * 2)), 16) + iv = urandom(GOST3412Magma.blocksize * 2) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = cbc_encrypt(ciph.encrypt, GOST3412Magma.blocksize, pt, iv) + self.assertSequenceEqual(cbc_decrypt( + ciph.decrypt, + GOST3412Magma.blocksize, + ct, + iv, + ), pt) + + def test_cfb_vectors(self): + iv = self.iv[:16] + ciphtext = "" + ciphtext += "db37e0e266903c83" + ciphtext += "0d46644c1f9a089c" + ciphtext += "24bdd2035315d38b" + ciphtext += "bcc0321421075505" + self.assertSequenceEqual( + hexenc(cfb_encrypt( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(cfb_decrypt( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + iv, + )), + self.plaintext, + ) + + def test_cfb_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Magma.blocksize * 2) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = cfb_encrypt(ciph.encrypt, GOST3412Magma.blocksize, pt, iv) + self.assertSequenceEqual(cfb_decrypt( + ciph.encrypt, + GOST3412Magma.blocksize, + ct, + iv, + ), pt) + + def test_mac_vectors(self): + k1, k2 = _mac_ks(self.ciph.encrypt, GOST3412Magma.blocksize) + self.assertSequenceEqual(hexenc(k1), "5f459b3342521424") + self.assertSequenceEqual(hexenc(k2), "be8b366684a42848") + self.assertSequenceEqual( + hexenc(mac( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + )[:4]), + "154e7210", + ) + + def test_mac_applies(self): + for _ in range(100): + data = urandom(randint(0, 16 * 2)) + ciph = GOST3412Magma(urandom(KEYSIZE)) + mac(ciph.encrypt, GOST3412Magma.blocksize, data) + + +class TestVectorACPKM(TestCase): + """Test vectors from Р 1323565.1.017-2018 + """ + key = hexdec("8899AABBCCDDEEFF0011223344556677FEDCBA98765432100123456789ABCDEF") + + def test_magma_ctr_acpkm(self): + key = acpkm(GOST3412Magma(self.key).encrypt, GOST3412Magma.blocksize) + self.assertSequenceEqual(key, hexdec("863EA017842C3D372B18A85A28E2317D74BEFC107720DE0C9E8AB974ABD00CA0")) + key = acpkm(GOST3412Magma(key).encrypt, GOST3412Magma.blocksize) + self.assertSequenceEqual(key, hexdec("49A5E2677DE555982B8AD5E826652D17EEC847BF5B3997A81CF7FE7F1187BD27")) + key = acpkm(GOST3412Magma(key).encrypt, GOST3412Magma.blocksize) + self.assertSequenceEqual(key, hexdec("3256BF3F97B5667426A9FB1C5EAABE41893CCDD5A868F9B63B0AA90720FA43C4")) + + def test_magma_ctr(self): + encrypter = GOST3412Magma(self.key).encrypt + plaintext = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A +11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 +22 33 44 55 66 77 88 99 + """.replace("\n", "").replace(" ", "")) + iv = hexdec("12345678") + ciphertext = hexdec(""" +2A B8 1D EE EB 1E 4C AB 68 E1 04 C4 BD 6B 94 EA +C7 2C 67 AF 6C 2E 5B 6B 0E AF B6 17 70 F1 B3 2E +A1 AE 71 14 9E ED 13 82 AB D4 67 18 06 72 EC 6F +84 A2 F1 5B 3F CA 72 C1 + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + ctr_acpkm( + GOST3412Magma, + encrypter, + bs=GOST3412Magma.blocksize, + section_size=GOST3412Magma.blocksize * 2, + data=plaintext, + iv=iv + ), + ciphertext, + ) + self.assertSequenceEqual( + ctr_acpkm( + GOST3412Magma, + encrypter, + bs=GOST3412Magma.blocksize, + section_size=GOST3412Magma.blocksize * 2, + data=ciphertext, + iv=iv + ), + plaintext, + ) + + def test_kuznechik_ctr_acpkm(self): + key = acpkm(GOST3412Kuznechik(self.key).encrypt, GOST3412Kuznechik.blocksize) + self.assertSequenceEqual(key, hexdec("2666ED40AE687811745CA0B448F57A7B390ADB5780307E8E9659AC403AE60C60")) + key = acpkm(GOST3412Kuznechik(key).encrypt, GOST3412Kuznechik.blocksize) + self.assertSequenceEqual(key, hexdec("BB3DD5402E999B7A3DEBB0DB45448EC530F07365DFEE3ABA8415F77AC8F34CE8")) + key = acpkm(GOST3412Kuznechik(key).encrypt, GOST3412Kuznechik.blocksize) + self.assertSequenceEqual(key, hexdec("23362FD553CAD2178299A5B5A2D4722E3BB83C730A8BF57CE2DD004017F8C565")) + + def test_kuznechik_ctr(self): + encrypter = GOST3412Kuznechik(self.key).encrypt + iv = hexdec("1234567890ABCEF0") + plaintext = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A +11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 +22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 +33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 +44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 33 +55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 33 44 + """.replace("\n", "").replace(" ", "")) + ciphertext = hexdec(""" +F1 95 D8 BE C1 0E D1 DB D5 7B 5F A2 40 BD A1 B8 +85 EE E7 33 F6 A1 3E 5D F3 3C E4 B3 3C 45 DE E4 +4B CE EB 8F 64 6F 4C 55 00 17 06 27 5E 85 E8 00 +58 7C 4D F5 68 D0 94 39 3E 48 34 AF D0 80 50 46 +CF 30 F5 76 86 AE EC E1 1C FC 6C 31 6B 8A 89 6E +DF FD 07 EC 81 36 36 46 0C 4F 3B 74 34 23 16 3E +64 09 A9 C2 82 FA C8 D4 69 D2 21 E7 FB D6 DE 5D + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + ctr_acpkm( + GOST3412Kuznechik, + encrypter, + bs=GOST3412Kuznechik.blocksize, + section_size=GOST3412Kuznechik.blocksize * 2, + data=plaintext, + iv=iv, + ), + ciphertext, + ) + self.assertSequenceEqual( + ctr_acpkm( + GOST3412Kuznechik, + encrypter, + bs=GOST3412Kuznechik.blocksize, + section_size=GOST3412Kuznechik.blocksize * 2, + data=ciphertext, + iv=iv, + ), + plaintext, + ) + + def test_magma_omac_1_5_blocks(self): + encrypter = GOST3412Magma(self.key).encrypt + key_section_size = 640 // 8 + self.assertSequenceEqual( + acpkm_master( + GOST3412Magma, + encrypter, + key_section_size=key_section_size, + bs=GOST3412Magma.blocksize, + keymat_len=KEYSIZE + GOST3412Magma.blocksize, + ), + hexdec("0DF2F5273DA328932AC49D81D36B2558A50DBF9BBCAC74A614B2CCB2F1CBCD8A70638E3DE8B3571E"), + ) + text = hexdec("1122334455667700FFEEDDCC") + self.assertSequenceEqual( + mac_acpkm_master( + GOST3412Magma, + encrypter, + key_section_size, + section_size=GOST3412Magma.blocksize * 2, + bs=GOST3412Magma.blocksize, + data=text, + ), + hexdec("A0540E3730ACBCF3"), + ) + + def test_magma_omac_5_blocks(self): + encrypter = GOST3412Magma(self.key).encrypt + key_section_size = 640 // 8 + self.assertSequenceEqual( + acpkm_master( + GOST3412Magma, + encrypter, + key_section_size=key_section_size, + bs=GOST3412Magma.blocksize, + keymat_len=3 * (KEYSIZE + GOST3412Magma.blocksize), + ), + hexdec(""" +0D F2 F5 27 3D A3 28 93 2A C4 9D 81 D3 6B 25 58 +A5 0D BF 9B BC AC 74 A6 14 B2 CC B2 F1 CB CD 8A +70 63 8E 3D E8 B3 57 1E 8D 38 26 D5 5E 63 A1 67 +E2 40 66 40 54 7B 9F 1F 5F 2B 43 61 2A AE AF DA +18 0B AC 86 04 DF A6 FE 53 C2 CE 27 0E 9C 9F 52 +68 D0 FD BF E1 A3 BD D9 BE 5B 96 D0 A1 20 23 48 +6E F1 71 0F 92 4A E0 31 30 52 CB 5F CA 0B 79 1E +1B AB E8 57 6D 0F E3 A8 + """.replace("\n", "").replace(" ", "")), + ) + text = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A +11 22 33 44 55 66 77 88 + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + mac_acpkm_master( + GOST3412Magma, + encrypter, + key_section_size, + section_size=GOST3412Magma.blocksize * 2, + bs=GOST3412Magma.blocksize, + data=text, + ), + hexdec("34008DAD5496BB8E"), + ) + + def test_kuznechik_omac_1_5_blocks(self): + encrypter = GOST3412Kuznechik(self.key).encrypt + key_section_size = 768 // 8 + self.assertSequenceEqual( + acpkm_master( + GOST3412Kuznechik, + encrypter, + key_section_size=key_section_size, + bs=GOST3412Kuznechik.blocksize, + keymat_len=KEYSIZE + GOST3412Kuznechik.blocksize, + ), + hexdec(""" +0C AB F1 F2 EF BC 4A C1 60 48 DF 1A 24 C6 05 B2 +C0 D1 67 3D 75 86 A8 EC 0D D4 2C 45 A4 F9 5B AE +0F 2E 26 17 E4 71 48 68 0F C3 E6 17 8D F2 C1 37 + """.replace("\n", "").replace(" ", "")) + ) + text = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + mac_acpkm_master( + GOST3412Kuznechik, + encrypter, + key_section_size, + section_size=GOST3412Kuznechik.blocksize * 2, + bs=GOST3412Kuznechik.blocksize, + data=text, + ), + hexdec("B5367F47B62B995EEB2A648C5843145E"), + ) + + def test_kuznechik_omac_5_blocks(self): + encrypter = GOST3412Kuznechik(self.key).encrypt + key_section_size = 768 // 8 + self.assertSequenceEqual( + acpkm_master( + GOST3412Kuznechik, + encrypter, + key_section_size=key_section_size, + bs=GOST3412Kuznechik.blocksize, + keymat_len=3 * (KEYSIZE + GOST3412Kuznechik.blocksize), + ), + hexdec(""" +0C AB F1 F2 EF BC 4A C1 60 48 DF 1A 24 C6 05 B2 +C0 D1 67 3D 75 86 A8 EC 0D D4 2C 45 A4 F9 5B AE +0F 2E 26 17 E4 71 48 68 0F C3 E6 17 8D F2 C1 37 +C9 DD A8 9C FF A4 91 FE AD D9 B3 EA B7 03 BB 31 +BC 7E 92 7F 04 94 72 9F 51 B4 9D 3D F9 C9 46 08 +00 FB BC F5 ED EE 61 0E A0 2F 01 09 3C 7B C7 42 +D7 D6 27 15 01 B1 77 77 52 63 C2 A3 49 5A 83 18 +A8 1C 79 A0 4F 29 66 0E A3 FD A8 74 C6 30 79 9E +14 2C 57 79 14 FE A9 0D 3B C2 50 2E 83 36 85 D9 + """.replace("\n", "").replace(" ", "")), + ) + text = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A +11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 +22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 +33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + mac_acpkm_master( + GOST3412Kuznechik, + encrypter, + key_section_size, + section_size=GOST3412Kuznechik.blocksize * 2, + bs=GOST3412Kuznechik.blocksize, + data=text, + ), + hexdec("FBB8DCEE45BEA67C35F58C5700898E5D"), + ) + + +class ISO10126Test(TestCase): + def test_symmetric(self): + for _ in range(100): + for blocksize in (GOST3412Magma.blocksize, GOST3412Kuznechik.blocksize): + data = urandom(randint(0, blocksize * 3)) + padded = pad_iso10126(data, blocksize) + self.assertSequenceEqual(unpad_iso10126(padded, blocksize), data) + with self.assertRaises(ValueError): + unpad_iso10126(padded[1:], blocksize) + + def test_small(self): + with self.assertRaises(ValueError): + unpad_iso10126(b"foobar\x00\x09", 8) diff --git a/pygost-5.13/build/lib/pygost/test_kdf.py b/pygost-5.13/build/lib/pygost/test_kdf.py new file mode 100644 index 0000000..6921cfc --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_kdf.py @@ -0,0 +1,58 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from unittest import TestCase + +from pygost.kdf import kdf_gostr3411_2012_256 +from pygost.kdf import kdf_tree_gostr3411_2012_256 +from pygost.utils import hexdec + + +class TestKDFGOSTR34112012256(TestCase): + def runTest(self): + self.assertEqual( + kdf_gostr3411_2012_256( + hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + hexdec("26bdb878"), + hexdec("af21434145656378"), + ), + hexdec("a1aa5f7de402d7b3d323f2991c8d4534013137010a83754fd0af6d7cd4922ed9"), + ) + + +class TestKDFTREEGOSTR34112012256(TestCase): + def runTest(self): + self.assertSequenceEqual( + kdf_tree_gostr3411_2012_256( + hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + hexdec("26bdb878"), + hexdec("af21434145656378"), + 1, + ), + (hexdec("a1aa5f7de402d7b3d323f2991c8d4534013137010a83754fd0af6d7cd4922ed9"),), + ) + self.assertSequenceEqual( + kdf_tree_gostr3411_2012_256( + hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + hexdec("26bdb878"), + hexdec("af21434145656378"), + 2, + ), + ( + hexdec("22b6837845c6bef65ea71672b265831086d3c76aebe6dae91cad51d83f79d16b"), + hexdec("074c9330599d7f8d712fca54392f4ddde93751206b3584c8f43f9e6dc51531f9"), + ), + ) diff --git a/pygost-5.13/build/lib/pygost/test_mgm.py b/pygost-5.13/build/lib/pygost/test_mgm.py new file mode 100644 index 0000000..e70a7f5 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_mgm.py @@ -0,0 +1,75 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from random import randint +from unittest import TestCase + +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3412 import KEYSIZE +from pygost.mgm import MGM +from pygost.mgm import nonce_prepare +from pygost.utils import hexdec + + +class TestVector(TestCase): + def runTest(self): + key = hexdec("8899AABBCCDDEEFF0011223344556677FEDCBA98765432100123456789ABCDEF") + ad = hexdec("0202020202020202010101010101010104040404040404040303030303030303EA0505050505050505") + plaintext = hexdec("1122334455667700FFEEDDCCBBAA998800112233445566778899AABBCCEEFF0A112233445566778899AABBCCEEFF0A002233445566778899AABBCCEEFF0A0011AABBCC") + mgm = MGM(GOST3412Kuznechik(key).encrypt, GOST3412Kuznechik.blocksize) + ciphertext = mgm.seal(plaintext[:16], plaintext, ad) + self.assertSequenceEqual(ciphertext[:len(plaintext)], hexdec("A9757B8147956E9055B8A33DE89F42FC8075D2212BF9FD5BD3F7069AADC16B39497AB15915A6BA85936B5D0EA9F6851CC60C14D4D3F883D0AB94420695C76DEB2C7552")) + self.assertSequenceEqual(ciphertext[len(plaintext):], hexdec("CF5D656F40C34F5C46E8BB0E29FCDB4C")) + self.assertSequenceEqual(mgm.open(plaintext[:16], ciphertext, ad), plaintext) + + +class TestSymmetric(TestCase): + def _itself(self, mgm, bs, tag_size): + for _ in range(1000): + nonce = nonce_prepare(urandom(bs)) + ad = urandom(randint(0, 20)) + pt = urandom(randint(0, 20)) + if len(ad) + len(pt) == 0: + continue + ct = mgm.seal(nonce, pt, ad) + self.assertEqual(len(ct) - tag_size, len(pt)) + self.assertSequenceEqual(mgm.open(nonce, ct, ad), pt) + + def test_magma(self): + for tag_size in ( + GOST3412Magma.blocksize, + GOST3412Magma.blocksize - 2, + ): + mgm = MGM( + GOST3412Magma(urandom(KEYSIZE)).encrypt, + GOST3412Magma.blocksize, + tag_size, + ) + self._itself(mgm, GOST3412Magma.blocksize, tag_size) + + def test_kuznechik(self): + for tag_size in ( + GOST3412Kuznechik.blocksize, + GOST3412Kuznechik.blocksize - 2, + ): + mgm = MGM( + GOST3412Kuznechik(urandom(KEYSIZE)).encrypt, + GOST3412Kuznechik.blocksize, + tag_size, + ) + self._itself(mgm, GOST3412Kuznechik.blocksize, tag_size) diff --git a/pygost-5.13/build/lib/pygost/test_pfx.py b/pygost-5.13/build/lib/pygost/test_pfx.py new file mode 100644 index 0000000..a2760bf --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_pfx.py @@ -0,0 +1,680 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from base64 import b64decode +from hmac import new as hmac_new +from unittest import skipIf +from unittest import TestCase + +from pygost import gost3410 +from pygost.gost28147 import cfb_decrypt +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.gost34112012512 import pbkdf2 as gost34112012_pbkdf2 +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3412 import KEYSIZE +from pygost.gost3413 import ctr_acpkm +from pygost.gost3413 import mac as omac +from pygost.kdf import kdf_tree_gostr3411_2012_256 +from pygost.kdf import keg +from pygost.utils import hexdec +from pygost.wrap import kimp15 + + +try: + from pyderasn import OctetString + + from pygost.asn1schemas.cms import EncryptedData + from pygost.asn1schemas.cms import EnvelopedData + from pygost.asn1schemas.cms import SignedAttributes + from pygost.asn1schemas.cms import SignedData + from pygost.asn1schemas.oids import id_data + from pygost.asn1schemas.oids import id_envelopedData + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_wrap_kexp15 + from pygost.asn1schemas.oids import id_messageDigest + from pygost.asn1schemas.oids import id_pbes2 + from pygost.asn1schemas.oids import id_pkcs12_bagtypes_certBag + from pygost.asn1schemas.oids import id_pkcs12_bagtypes_keyBag + from pygost.asn1schemas.oids import id_pkcs12_bagtypes_pkcs8ShroudedKeyBag + from pygost.asn1schemas.oids import id_pkcs9_certTypes_x509Certificate + from pygost.asn1schemas.oids import id_signedData + from pygost.asn1schemas.oids import id_tc26_agreement_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256 + from pygost.asn1schemas.pfx import CertBag + from pygost.asn1schemas.pfx import KeyBag + from pygost.asn1schemas.pfx import OctetStringSafeContents + from pygost.asn1schemas.pfx import PBES2Params + from pygost.asn1schemas.pfx import PFX + from pygost.asn1schemas.pfx import PKCS8ShroudedKeyBag + from pygost.asn1schemas.pfx import SafeContents + from pygost.asn1schemas.x509 import Certificate +except ImportError: + pyderasn_exists = False +else: + pyderasn_exists = True + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestPFX(TestCase): + """PFX test vectors from "Транспортный ключевой контейнер" (R50.1.112-2016.pdf) + """ + pfx_raw = b64decode(""" +MIIFqgIBAzCCBSsGCSqGSIb3DQEHAaCCBRwEggUYMIIFFDCCASIGCSqGSIb3DQEH +AaCCARMEggEPMIIBCzCCAQcGCyqGSIb3DQEMCgECoIHgMIHdMHEGCSqGSIb3DQEF +DTBkMEEGCSqGSIb3DQEFDDA0BCD5qZr0TTIsBvdgUoq/zFwOzdyJohj6/4Wiyccg +j9AK/QICB9AwDAYIKoUDBwEBBAIFADAfBgYqhQMCAhUwFQQI3Ip/Vp0IsyIGCSqF +AwcBAgUBAQRoSfLhgx9s/zn+BjnhT0ror07vS55Ys5hgvVpWDx4mXGWWyez/2sMc +aFgSr4H4UTGGwoMynGLpF1IOVo+bGJ0ePqHB+gS5OL9oV+PUmZ/ELrRENKlCDqfY +WvpSystX29CvCFrnTnDsbBYxFTATBgkqhkiG9w0BCRUxBgQEAQAAADCCA+oGCSqG +SIb3DQEHBqCCA9swggPXAgEAMIID0AYJKoZIhvcNAQcBMHEGCSqGSIb3DQEFDTBk +MEEGCSqGSIb3DQEFDDA0BCCJTJLZQRi1WIpQHzyjXbq7+Vw2+1280C45x8ff6kMS +VAICB9AwDAYIKoUDBwEBBAIFADAfBgYqhQMCAhUwFQQIxepowwvS11MGCSqFAwcB +AgUBAYCCA06n09P/o+eDEKoSWpvlpOLKs7dKmVquKzJ81nCngvLQ5fEWL1WkxwiI +rEhm53JKLD0wy4hekalEk011Bvc51XP9gkDkmaoBpnV/TyKIY35wl6ATfeGXno1M +KoA+Ktdhv4gLnz0k2SXdkUj11JwYskXue+REA0p4m2ZsoaTmvoODamh9JeY/5Qjy +Xe58CGnyXFzX3eU86qs4WfdWdS3NzYYOk9zzVl46le9u79O/LnW2j4n2of/Jpk/L +YjrRmz5oYeQOqKOKhEyhpO6e+ejr6laduEv7TwJQKRNiygogbVvkNn3VjHTSOUG4 +W+3NRPhjb0jD9obdyx6MWa6O3B9bUzFMNav8/gYn0vTDxqXMLy/92oTngNrVx6Gc +cNl128ISrDS6+RxtAMiEBRK6xNkemqX5yNXG5GrLQQFGP6mbs2nNpjKlgj3pljmX +Eky2/G78XiJrv02OgGs6CKnI9nMpa6N7PBHV34MJ6EZzWOWDRQ420xk63mnicrs0 +WDVJ0xjdu4FW3iEk02EaiRTvGBpa6GL7LBp6QlaXSSwONx725cyRsL9cTlukqXER +WHDlMpjYLbkGZRrCc1myWgEfsputfSIPNF/oLv9kJNWacP3uuDOfecg3us7eg2OA +xo5zrYfn39GcBMF1WHAYRO/+PnJb9jrDuLAE8+ONNqjNulWNK9CStEhb6Te+yE6q +oeP6hJjFLi+nFLE9ymIo0A7gLQD5vzFvl+7v1ZNVnQkwRUsWoRiEVVGnv3Z1iZU6 +xStxgoHMl62V/P5cz4dr9vJM2adEWNZcVXl6mk1H8DRc1sRGnvs2l237oKWRVntJ +hoWnZ8qtD+3ZUqsX79QhVzUQBzKuBt6jwNhaHLGl5B+Or/zA9FezsOh6+Uc+fZaV +W7fFfeUyWwGy90XD3ybTrjzep9f3nt55Z2c+fu2iEwhoyImWLuC3+CVhf9Af59j9 +8/BophMJuATDJEtgi8rt4vLnfxKu250Mv2ZpbfF69EGTgFYbwc55zRfaUG9zlyCu +1YwMJ6HC9FUVtJp9gObSrirbzTH7mVaMjQkBLotazWbegzI+be8V3yT06C+ehD+2 +GdLWAVs9hp8gPHEUShb/XrgPpDSJmFlOiyeOFBO/j4edDACKqVcwdjBOMAoGCCqF +AwcBAQIDBEAIFX0fyZe20QKKhWm6WYX+S92Gt6zaXroXOvAmayzLfZ5Sd9C2t9zZ +JSg6M8RBUYpw/8ym5ou1o2nDa09M5zF3BCCpzyCQBI+rzfISeKvPV1ROfcXiYU93 +mwcl1xQV2G5/fgICB9A= + """) + password = u"Пароль для PFX" + + def test_shrouded_key_bag(self): + private_key_info_expected = b64decode(b""" +MGYCAQAwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEBAgIEQEYbRu86z+1JFKDcPDN9UbTG +G2ki9enTqos4KpUU0j9IDpl1UXiaA1YDIwUjlAp+81GkLmyt8Fw6Gt/X5JZySAY= + """) + + pfx, tail = PFX().decode(self.pfx_raw) + self.assertSequenceEqual(tail, b"") + _, outer_safe_contents = pfx["authSafe"]["content"].defined + safe_contents, tail = OctetStringSafeContents().decode( + bytes(outer_safe_contents[0]["bagValue"]), + ) + self.assertSequenceEqual(tail, b"") + safe_bag = safe_contents[0] + shrouded_key_bag, tail = PKCS8ShroudedKeyBag().decode( + bytes(safe_bag["bagValue"]), + ) + self.assertSequenceEqual(tail, b"") + _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + + key = gost34112012_pbkdf2( + password=self.password.encode("utf-8"), + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("309dd0354c5603739403f2335e9e2055138f8b5c98b63009de0635eea1fd7ba8") + self.assertSequenceEqual( + cfb_decrypt( + key, + bytes(shrouded_key_bag["encryptedData"]), + iv=bytes(enc_scheme_params["iv"]), + sbox="id-tc26-gost-28147-param-Z", + ), + private_key_info_expected, + ) + + def test_encrypted_data(self): + cert_bag_expected = b64decode(b""" +MIIDSjCCA0YGCyqGSIb3DQEMCgEDoIIDHjCCAxoGCiqGSIb3DQEJFgGgggMKBIIDBjCCAwIwggKt +oAMCAQICEAHQaF8xH5bAAAAACycJAAEwDAYIKoUDBwEBAwIFADBgMQswCQYDVQQGEwJSVTEVMBMG +A1UEBwwM0JzQvtGB0LrQstCwMQ8wDQYDVQQKDAbQotCaMjYxKTAnBgNVBAMMIENBIGNlcnRpZmlj +YXRlIChQS0NTIzEyIGV4YW1wbGUpMB4XDTE1MDMyNzA3MjUwMFoXDTIwMDMyNzA3MjMwMFowZDEL +MAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MS0wKwYD +VQQDDCRUZXN0IGNlcnRpZmljYXRlIDEgKFBLQ1MjMTIgZXhhbXBsZSkwZjAfBggqhQMHAQEBATAT +BgcqhQMCAiMBBggqhQMHAQECAgNDAARA1xzymkpvr2dYJT8WTOX3Dt96/+hGsXNytUQpkWB5ImJM +4tg9AsC4RIUwV5H41MhG0uBRFweTzN6AsAdBvhTClYEJADI3MDkwMDAxo4IBKTCCASUwKwYDVR0Q +BCQwIoAPMjAxNTAzMjcwNzI1MDBagQ8yMDE2MDMyNzA3MjUwMFowDgYDVR0PAQH/BAQDAgTwMB0G +A1UdDgQWBBQhWOsRQ68yYN2Utg/owHoWcqsVbTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwQwDAYDVR0TAQH/BAIwADCBmQYDVR0jBIGRMIGOgBQmnc7Xh5ykb5t/BMwOkxA4drfEmqFkpGIw +YDELMAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MSkw +JwYDVQQDDCBDQSBjZXJ0aWZpY2F0ZSAoUEtDUyMxMiBleGFtcGxlKYIQAdBoXvL8TSAAAAALJwkA +ATAMBggqhQMHAQEDAgUAA0EA9oq0Vvk8kkgIwkp0x0J5eKtia4MNTiwKAm7jgnCZIx3O98BThaTX +3ZQhEo2RL9pTCPr6wFMheeJ+YdGMReXvsjEVMBMGCSqGSIb3DQEJFTEGBAQBAAAA + """) + + pfx, tail = PFX().decode(self.pfx_raw) + self.assertSequenceEqual(tail, b"") + _, outer_safe_contents = pfx["authSafe"]["content"].defined + _, encrypted_data = outer_safe_contents[1]["bagValue"].defined + _, pbes2_params = encrypted_data["encryptedContentInfo"]["contentEncryptionAlgorithm"]["parameters"].defined + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + key = gost34112012_pbkdf2( + password=self.password.encode("utf-8"), + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("0e93d71339e7f53b79a0bc41f9109dd4fb60b30ae10736c1bb77b84c07681cfc") + self.assertSequenceEqual( + cfb_decrypt( + key, + bytes(encrypted_data["encryptedContentInfo"]["encryptedContent"]), + iv=bytes(enc_scheme_params["iv"]), + sbox="id-tc26-gost-28147-param-Z", + ), + cert_bag_expected, + ) + + def test_mac(self): + pfx, tail = PFX().decode(self.pfx_raw) + self.assertSequenceEqual(tail, b"") + _, outer_safe_contents = pfx["authSafe"]["content"].defined + mac_data = pfx["macData"] + mac_key = gost34112012_pbkdf2( + password=self.password.encode("utf-8"), + salt=bytes(mac_data["macSalt"]), + iterations=int(mac_data["iterations"]), + dklen=96, + )[-32:] + # mac_key = hexdec("cadbfbf3bceaa9b79f651508fac5abbeb4a13d0bd0e1876bd3c3efb2112128a5") + self.assertSequenceEqual( + hmac_new( + key=mac_key, + msg=SafeContents(outer_safe_contents).encode(), + digestmod=GOST34112012512, + ).digest(), + bytes(mac_data["mac"]["digest"]), + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestPFX2020(TestCase): + """PFX test vectors from newer PKCS#12 update + """ + ca_prv_raw = hexdec("092F8D059E97E22B90B1AE99F0087FC4D26620B91550CBB437C191005A290810") + ca_curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"] + ca_cert = Certificate().decod(b64decode(b""" + MIIB+TCCAaagAwIBAgIEAYy6gTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS + cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx + MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx + 5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMHAQEBATALBgkq + hQMHAQIBAQEDQwAEQBpKgpyPDnhQAJyLqy8Qs0XQhgxEhby6tSypqYimgbjpcKqtU6 + 4jpDXc3h3BxGxtl2oHJ/4YLZ/ll87dto3ltMqjgZgwgZUwYwYDVR0jBFwwWoAUrGwO + TERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHk + NBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUrGwO + TERmokKW4p8JOyVm88ukUyowDwYDVR0TAQH/BAUwAwEB/zAKBggqhQMHAQEDAgNBAB + Gg3nhgQ5oCKbqlEdVaRxH+1WX4wVkawGXuTYkr1AC2OWw3ZC14Vvg3nazm8UMWUZtk + vu1kJcHQ4jFKkjUeg2E= + """)) + ca_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes( + ca_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"] + )))) + password = u"Пароль для PFX".encode("utf-8") + cert_test = Certificate().decod(b64decode(b""" + MIICLjCCAdugAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS + cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx + MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYDVQQDEy + FPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQwgaAwFwYIKoUDBwEBAQIw + CwYJKoUDBwECAQIBA4GEAASBgLSLt1q8KQ4YZVxioU+1LV9QhE7MHR9gBEh7S1yVNG + lqt7+rNG5VFqmrPM74rbUsOlhV8M+zZKprXdk35Oz8lSW/n2oIUHZxikXIH/SSHj4r + v3K/Puvz7hYTQSZl/xPdp78nUmjrEa6d5wfX8biEy2z0dgufFvAkMw1Ua4gdXqDOo4 + GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4MQ0wCwYD + VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaX + SCBAGMuoEwHQYDVR0OBBYEFH4GVwmYDK1rCKhX7nkAWDrJ16CkMAoGCCqFAwcBAQMC + A0EACl6p8dAbpi9Hk+3mgMyI0WIh17IrlrSp/mB0F7ZzMt8XUD1Dwz3JrrnxeXnfMv + OA5BdUJ9hCyDgMVAGs/IcEEA== + """)) + prv_test_raw = b64decode(""" + MIHiAgEBMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQRAEWkl+eblsHWs86SNgRKq + SxMOgGhbvR/uZ5/WWfdNG1axvUwVhpcXIxDZUmzQuNzqJBkseI7f5/JjXyTFRF1a + +YGBgQG0i7davCkOGGVcYqFPtS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO + +K21LDpYVfDPs2Sqa13ZN+Ts/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0Em + Zf8T3ae/J1Jo6xGunecH1/G4hMts9HYLnxbwJDMNVGuIHV6gzg== + """) + + def test_cert_and_encrypted_key(self): + pfx_raw = b64decode(b""" + MIIFKwIBAzCCBMQGCSqGSIb3DQEHAaCCBLUEggSxMIIErTCCAswGCSqGSIb3DQEH + AaCCAr0EggK5MIICtTCCArEGCyqGSIb3DQEMCgEDoIICSjCCAkYGCiqGSIb3DQEJ + FgGgggI2BIICMjCCAi4wggHboAMCAQICBAGMuoQwCgYIKoUDBwEBAwIwODENMAsG + A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt + Yml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UEChME + VEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiA1MTItYml0 + MIGgMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQOBhAAEgYC0i7davCkOGGVcYqFP + tS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDPs2Sqa13ZN+Ts + /JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo6xGunecH1/G4 + hMts9HYLnxbwJDMNVGuIHV6gzqOBhzCBhDBjBgNVHSMEXDBagBSsbA5MRGaiQpbi + nwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsy + NjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBR+BlcJmAyt + awioV+55AFg6ydegpDAKBggqhQMHAQEDAgNBAApeqfHQG6YvR5Pt5oDMiNFiIdey + K5a0qf5gdBe2czLfF1A9Q8M9ya658Xl53zLzgOQXVCfYQsg4DFQBrPyHBBAxVDAj + BgkqhkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkU + MSAeHgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTCCAdkGCSqGSIb3DQEH + AaCCAcoEggHGMIIBwjCCAb4GCyqGSIb3DQEMCgECoIIBVzCCAVMwWQYJKoZIhvcN + AQUNMEwwKQYJKoZIhvcNAQUMMBwECKf4N7NMwugqAgIIADAMBggqhQMHAQEEAgUA + MB8GCSqFAwcBAQUCAjASBBAlmt2WDfaPJlsAs0mLKglzBIH1DMvEacbbWRNDVSnX + JLWygYrKoipdOjDA/2HEnBZ34uFOLNheUqiKpCPoFpbR2GBiVYVTVK9ibiczgaca + EQYzDXtcS0QCZOxpKWfteAlbdJLC/SqPurPYyKi0MVRUPROhbisFASDT38HDH1Dh + 0dL5f6ga4aPWLrWbbgWERFOoOPyh4DotlPF37AQOwiEjsbyyRHq3HgbWiaxQRuAh + eqHOn4QVGY92/HFvJ7u3TcnQdLWhTe/lh1RHLNF3RnXtN9if9zC23laDZOiWZplU + yLrUiTCbHrtn1RppPDmLFNMt9dJ7KKgCkOi7Zm5nhqPChbywX13wcfYxVDAjBgkq + hkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkUMSAe + HgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTBeME4wCgYIKoUDBwEBAgME + QAkBKw4ihn7pSIYTEhu0bcvTPZjI3WgVxCkUVlOsc80G69EKFEOTnObGJGSKJ51U + KkOsXF0a7+VBZf3BcVVQh9UECIVEtO+VpuskAgIIAA== + """) + pfx = PFX().decod(pfx_raw) + _, outer_safe_contents = pfx["authSafe"]["content"].defined + + safe_contents = OctetStringSafeContents().decod(bytes( + outer_safe_contents[0]["bagValue"] + )) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag) + cert_bag = CertBag().decod(bytes(safe_bag["bagValue"])) + self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate) + _, cert = cert_bag["certValue"].defined + self.assertEqual(Certificate(cert), self.cert_test) + + safe_contents = OctetStringSafeContents().decod(bytes( + outer_safe_contents[1]["bagValue"] + )) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag) + shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"])) + _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + ukm = bytes(enc_scheme_params["ukm"]) + key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("4b7ae649ca31dd5fe3243a91a5188c03f1d7049bec8e0d241c0e1e8c39ea4c1f") + key_enc, key_mac = kdf_tree_gostr3411_2012_256( + key, b"kdf tree", ukm[GOST3412Kuznechik.blocksize // 2:], 2, + ) + ciphertext = bytes(shrouded_key_bag["encryptedData"]) + plaintext = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(key_enc).encrypt, + section_size=256 * 1024, + bs=GOST3412Kuznechik.blocksize, + data=ciphertext, + iv=ukm[:GOST3412Kuznechik.blocksize // 2], + ) + mac_expected = plaintext[-GOST3412Kuznechik.blocksize:] + plaintext = plaintext[:-GOST3412Kuznechik.blocksize] + mac = omac( + GOST3412Kuznechik(key_mac).encrypt, + GOST3412Kuznechik.blocksize, + plaintext, + ) + self.assertSequenceEqual(mac, mac_expected) + self.assertSequenceEqual(plaintext, self.prv_test_raw) + + mac_data = pfx["macData"] + mac_key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(mac_data["macSalt"]), + iterations=int(mac_data["iterations"]), + dklen=96, + )[-32:] + # mac_key = hexdec("a81d1bc91a4a5cf1fd7320f92dda7e5b285816c3b20826a382d7ed0cbf3a9bf4") + self.assertSequenceEqual( + hmac_new( + key=mac_key, + msg=SafeContents(outer_safe_contents).encode(), + digestmod=GOST34112012512, + ).digest(), + bytes(mac_data["mac"]["digest"]), + ) + self.assertTrue(gost3410.verify( + self.ca_curve, + self.ca_pub, + GOST34112012256(cert["tbsCertificate"].encode()).digest()[::-1], + bytes(cert["signatureValue"]), + )) + + def test_encrypted_cert_and_key(self): + pfx_raw = b64decode(b""" + MIIFjAIBAzCCBSUGCSqGSIb3DQEHAaCCBRYEggUSMIIFDjCCA0EGCSqGSIb3DQEH + BqCCAzIwggMuAgEAMIIDJwYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBIMCkGCSqG + SIb3DQEFDDAcBAgUuSVGsSwGjQICCAAwDAYIKoUDBwEBBAIFADAbBgkqhQMHAQEF + AQIwDgQM9Hk3dagtS48+G/x+gIICwWGPqxxN+sTrKbruRf9R5Ya9cf5AtO1frqMn + f1eULfmZmTg/BdE51QQ+Vbnh3v1kmspr6h2+e4Wli+ndEeCWG6A6X/G22h/RAHW2 + YrVmf6cCWxW+YrqzT4h/8RQL/9haunD5LmHPLVsYrEai0OwbgXayDSwARVJQLQYq + sLNmZK5ViN+fRiS5wszVJ3AtVq8EuPt41aQEKwPy2gmH4S6WmnQRC6W7aoqmIifF + PJENJNn5K2M1J6zNESs6bFtYNKMArNqtvv3rioY6eAaaLy6AV6ljsekmqodHmQjv + Y4eEioJs0xhpXhZY69PXT+ZBeHv6MSheBhwXqxAd1DqtPTafMjNK8rqKCap9TtPG + vONvo5W9dgwegxRRQzlum8dzV4m1W9Aq4W7t8/UcxDWRz3k6ijFPlGaA9+8ZMTEO + RHhBRvM6OY2/VNNxbgxWfGYuPxpSi3YnCZIPmBEe5lU/Xv7KjzFusGM38F8YR61k + 4/QNpKI1QUv714YKfaUQznshGGzILv1NGID62pl1+JI3vuawi2mDMrmkuM9QFU9v + /kRP+c2uBHDuOGEUUSNhF08p7+w3vxplatGWXH9fmIsPBdk2f3wkn+rwoqrEuijM + I/bCAylU/M0DMKhAo9j31UYSZdi4fsfRWYDJMq/8FPn96tuo+oCpbqv3NUwpZM/8 + Li4xqgTHtYw/+fRG0/P6XadNEiII/TYjenLfVHXjAHOVJsVeCu/t3EsMYHQddNCh + rFk/Ic2PdIQOyB4/enpW0qrKegSbyZNuF1WI4zl4mI89L8dTQBUkhy45yQXZlDD8 + k1ErYdtdEsPtz/4zuSpbnmwCEIRoOuSXtGuJP+tbcWEXRKM2UBgi3qBjpn7DU18M + tsrRM9pDdadl8mT/Vfh9+B8dZBZVxgQu70lMPEGexbUkYHuFCCnyi9J0V92StbIz + Elxla1VebjCCAcUGCSqGSIb3DQEHAaCCAbYEggGyMIIBrjCCAaoGCyqGSIb3DQEM + CgECoIIBQzCCAT8wVQYJKoZIhvcNAQUNMEgwKQYJKoZIhvcNAQUMMBwECP0EQk0O + 1twvAgIIADAMBggqhQMHAQEEAgUAMBsGCSqFAwcBAQUBATAOBAzwxSqgAAAAAAAA + AAAEgeUqj9mI3RDfK5hMd0EeYws7foZK/5ANr2wUhP5qnDjAZgn76lExJ+wuvlnS + 9PChfWVugvdl/9XJgQvvr9Cu4pOh4ICXplchcy0dGk/MzItHRVC5wK2nTxwQ4kKT + kG9xhLFzoD16dhtqX0+/dQg9G8pE5EzCBIYRXLm1Arcz9k7KVsTJuNMjFrr7EQuu + Tr80ATSQOtsq50zpFyrpznVPGCrOdIjpymZxNdvw48bZxqTtRVDxCYATOGqz0pwH + ClWULHD9LIajLMB2GhBKyQw6ujIlltJs0T+WNdX/AT2FLi1LFSS3+Cj9MVQwIwYJ + KoZIhvcNAQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMC0GCSqGSIb3DQEJFDEg + Hh4AcAAxADIARgByAGkAZQBuAGQAbAB5AE4AYQBtAGUwXjBOMAoGCCqFAwcBAQID + BEDp4e22JmXdnvR0xA99yQuzQuJ8pxBeOpsLm2dZQqt3Fje5zqW1uk/7VOcfV5r2 + bKm8nsLOs2rPT8hBOoeAZvOIBAjGIUHw6IjG2QICCAA= + """) + pfx = PFX().decod(pfx_raw) + _, outer_safe_contents = pfx["authSafe"]["content"].defined + + encrypted_data = EncryptedData().decod(bytes( + outer_safe_contents[0]["bagValue"] + )) + eci = encrypted_data["encryptedContentInfo"] + self.assertEqual(eci["contentEncryptionAlgorithm"]["algorithm"], id_pbes2) + pbes2_params = PBES2Params().decod(bytes( + eci["contentEncryptionAlgorithm"]["parameters"] + )) + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + ukm = bytes(enc_scheme_params["ukm"]) + key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("d066a96fb326ba896a2352d3f40240a4ded6e7e7bd5b4db6b5241d631c8c381c") + key_enc, key_mac = kdf_tree_gostr3411_2012_256( + key, b"kdf tree", ukm[GOST3412Magma.blocksize // 2:], 2, + ) + ciphertext = bytes(eci["encryptedContent"]) + plaintext = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(key_enc).encrypt, + section_size=8 * 1024, + bs=GOST3412Magma.blocksize, + data=ciphertext, + iv=ukm[:GOST3412Magma.blocksize // 2], + ) + mac_expected = plaintext[-GOST3412Magma.blocksize:] + plaintext = plaintext[:-GOST3412Magma.blocksize] + mac = omac( + GOST3412Magma(key_mac).encrypt, + GOST3412Magma.blocksize, + plaintext, + ) + self.assertSequenceEqual(mac, mac_expected) + + safe_contents = SafeContents().decod(plaintext) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag) + cert_bag = CertBag().decod(bytes(safe_bag["bagValue"])) + self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate) + _, cert = cert_bag["certValue"].defined + self.assertEqual(Certificate(cert), self.cert_test) + + safe_contents = OctetStringSafeContents().decod(bytes( + outer_safe_contents[1]["bagValue"] + )) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag) + shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"])) + _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + ukm = bytes(enc_scheme_params["ukm"]) + key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("f840d001fd11441e0fb7ccf48f471915e5bf35275309dbe7ade9da4fe460ba7e") + ciphertext = bytes(shrouded_key_bag["encryptedData"]) + plaintext = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(key).encrypt, + section_size=8 * 1024, + bs=GOST3412Magma.blocksize, + data=ciphertext, + iv=ukm[:GOST3412Magma.blocksize // 2], + ) + self.assertSequenceEqual(plaintext, self.prv_test_raw) + + mac_data = pfx["macData"] + mac_key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(mac_data["macSalt"]), + iterations=int(mac_data["iterations"]), + dklen=96, + )[-32:] + # mac_key = hexdec("084f81782af1534ffd67e3c579c14cb45d7a6f659f46fdbb51a552e874e66fb2") + self.assertSequenceEqual( + hmac_new( + key=mac_key, + msg=SafeContents(outer_safe_contents).encode(), + digestmod=GOST34112012512, + ).digest(), + bytes(mac_data["mac"]["digest"]), + ) + + def test_dh(self): + curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"] + # sender_prv_raw = hexdec("0B20810E449978C7C3B76C6FF77A16C532421139344A058EF56310B6B6F377E8") + sender_cert = Certificate().decod(b64decode(""" + MIIB6zCCAZigAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 + MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw + MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD + VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMH + AQEBATALBgkqhQMHAQIBAQEDQwAEQJYpDRNiWWqDgaZje0EmLLOldQ35o5X1ZuZN + SKequYQc/soI3OgDMWD7ThJJCk01IelCeb6MsBmG4lol+pnpVtOjgYcwgYQwYwYD + VR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRL + MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6 + gTAdBgNVHQ4EFgQUPx5RgcjkifhlJm4/jQdkbm30rVQwCgYIKoUDBwEBAwIDQQA6 + 8x7Vk6PvP/8xOGHhf8PuqaXAYskSyJPuBu+3Bo/PEj10devwc1J9uYWIDCGdKKPy + bSlnQHqUPBBPM30YX1YN + """)) + recipient_prv_raw = hexdec("0DC8DC1FF2BC114BABC3F1CA8C51E4F58610427E197B1C2FBDBA4AE58CBFB7CE")[::-1] + recipient_prv = gost3410.prv_unmarshal(recipient_prv_raw) + recipient_cert = Certificate().decod(b64decode(""" + MIIB6jCCAZegAwIBAgIEAYy6gzAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 + MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw + MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD + VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDBeMBcGCCqFAwcB + AQEBMAsGCSqFAwcBAgEBAQNDAARAvyeCGXMsYwpYe5aE0w8w3m4vpKQapGInqpnF + lv7h08psFP0s1W80q3BR534F4TmR+o5+iU+AW6ycvWuc73JEQ6OBhzCBhDBjBgNV + HSMEXDBagBSsbA5MRGaiQpbinwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsy + NjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqB + MB0GA1UdDgQWBBQ35gHPN1bx8l2eEMTbrtIg+5MU0TAKBggqhQMHAQEDAgNBABF2 + RHDaRqQuBS2yu7yGIGFgA6c/LG4GKjSOwYsRVmXJNNkQ4TB7PB8j3q7gx2koPsVB + m90WfMWSL6SNSh3muuM= + """)) + self.assertTrue(gost3410.verify( + self.ca_curve, + self.ca_pub, + GOST34112012256(sender_cert["tbsCertificate"].encode()).digest()[::-1], + bytes(sender_cert["signatureValue"]), + )) + self.assertTrue(gost3410.verify( + self.ca_curve, + self.ca_pub, + GOST34112012256(recipient_cert["tbsCertificate"].encode()).digest()[::-1], + bytes(recipient_cert["signatureValue"]), + )) + + pfx_raw = b64decode(""" + MIIKVwIBAzCCClAGCSqGSIb3DQEHAqCCCkEwggo9AgEBMQwwCgYIKoUDBwEBAgIw + ggcjBgkqhkiG9w0BBwGgggcUBIIHEDCCBwwwggKdBgkqhkiG9w0BBwGgggKOBIIC + ijCCAoYwggKCBgsqhkiG9w0BDAoBA6CCAkowggJGBgoqhkiG9w0BCRYBoIICNgSC + AjIwggIuMIIB26ADAgECAgQBjLqEMAoGCCqFAwcBAQMCMDgxDTALBgNVBAoTBFRL + MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDAeFw0w + MTAxMDEwMDAwMDBaFw00OTEyMzEwMDAwMDBaMDsxDTALBgNVBAoTBFRLMjYxKjAo + BgNVBAMTIU9SSUdJTkFUT1I6IEdPU1QgMzQuMTAtMTIgNTEyLWJpdDCBoDAXBggq + hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAtIu3WrwpDhhlXGKhT7UtX1CETswd + H2AESHtLXJU0aWq3v6s0blUWqas8zvittSw6WFXwz7Nkqmtd2Tfk7PyVJb+faghQ + dnGKRcgf9JIePiu/cr8+6/PuFhNBJmX/E92nvydSaOsRrp3nB9fxuITLbPR2C58W + 8CQzDVRriB1eoM6jgYcwgYQwYwYDVR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88uk + UyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg + MzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUfgZXCZgMrWsIqFfueQBY + OsnXoKQwCgYIKoUDBwEBAwIDQQAKXqnx0BumL0eT7eaAzIjRYiHXsiuWtKn+YHQX + tnMy3xdQPUPDPcmuufF5ed8y84DkF1Qn2ELIOAxUAaz8hwQQMSUwIwYJKoZIhvcN + AQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMIIEZwYJKoZIhvcNAQcDoIIEWDCC + BFQCAQKgggHzoIIB7zCCAeswggGYoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODEN + MAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAy + NTYtYml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UE + ChMEVEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYt + Yml0MF4wFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBA0MABECWKQ0TYllqg4GmY3tB + JiyzpXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ + 6VbTo4GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4 + MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEy + IDI1Ni1iaXSCBAGMuoEwHQYDVR0OBBYEFD8eUYHI5In4ZSZuP40HZG5t9K1UMAoG + CCqFAwcBAQMCA0EAOvMe1ZOj7z//MThh4X/D7qmlwGLJEsiT7gbvtwaPzxI9dHXr + 8HNSfbmFiAwhnSij8m0pZ0B6lDwQTzN9GF9WDTGB/6GB/AIBA6BCMEAwODENMAsG + A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt + Yml0AgQBjLqCoSIEIBt4fjey+k8C1D3OaMca8wl6h3j3C6OAbrx8rmxXktsQMBcG + CSqFAwcBAQcCATAKBggqhQMHAQEGATB2MHQwQDA4MQ0wCwYDVQQKEwRUSzI2MScw + JQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQCBAGMuoMEMJkp + Wae6IVfaY3mP0izRY7ifc41fATXdJ2tmTl+1vitkSE2vLCKXDLl90KfHA6gNmDCC + AVQGCSqGSIb3DQEHATAfBgkqhQMHAQEFAgEwEgQQFhEshEBO2LkAAAAAAAAAAICC + ASQYvLpT/8azEXJfekyGuyvE9UkVX+Ao8sfu9My/c4WAVRNMhZkCqD+BbPwBsIzN + sXZIi9rXGAfsPz7xaO9EUFZPjNOWtF/E01oJgG+gYLFn7qAiEFcmRLptSHuanNHn + 7Yol6IHushX4UaW9hEa/L6eFQx/hoDhrNZnWTXNZtNuHuMGC9dzhHhTxfkdjZYXD + v+M7psVj58JutE3U2d4pgxKcBPdMO4vl4+27cIKxQZFZU2zuCVJLYLqmPT5pCBkM + mJqy7bZwHOJ9kBq/TGUf8iJGYSCNre3RTNLbcTTk7rZrbiMkFsG3borzenpouS5E + BcCkBt8Mj0nvsMCu9ipHTuWww7LltlkXCjlNXFUi6ZI3VyHW5CDpghujQWiZxiAc + JuGl6GwZoIIB7zCCAeswggGYoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODENMAsG + A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt + Yml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UEChME + VEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYtYml0 + MF4wFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBA0MABECWKQ0TYllqg4GmY3tBJiyz + pXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ6VbT + o4GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4MQ0w + CwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1 + Ni1iaXSCBAGMuoEwHQYDVR0OBBYEFD8eUYHI5In4ZSZuP40HZG5t9K1UMAoGCCqF + AwcBAQMCA0EAOvMe1ZOj7z//MThh4X/D7qmlwGLJEsiT7gbvtwaPzxI9dHXr8HNS + fbmFiAwhnSij8m0pZ0B6lDwQTzN9GF9WDTGCAQ4wggEKAgEBMEAwODENMAsGA1UE + ChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0 + AgQBjLqCMAoGCCqFAwcBAQICoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc + BgkqhkiG9w0BCQUxDxcNMjEwNDE0MTkyMTEyWjAvBgkqhkiG9w0BCQQxIgQg1XOA + zNa710QuXsn5+yIf3cNTiFOQMgTiBRJBz8Tr4I0wCgYIKoUDBwEBAQEEQALINal9 + 7wHXYiG+w0yzSkKOs0jRZew0S73r/cfk/sUoM3HKKIEbKruvlAdiOqX/HLFSEx/s + kxFG6QUFH8uuoX8= + """) + pfx = PFX().decod(pfx_raw) + self.assertEqual(pfx["authSafe"]["contentType"], id_signedData) + + sd = SignedData().decod(bytes(pfx["authSafe"]["content"])) + self.assertEqual(sd["certificates"][0]["certificate"], sender_cert) + si = sd["signerInfos"][0] + self.assertEqual( + si["digestAlgorithm"]["algorithm"], + id_tc26_gost3411_2012_256, + ) + digest = [ + bytes(attr["attrValues"][0].defined[1]) for attr in si["signedAttrs"] + if attr["attrType"] == id_messageDigest + ][0] + sender_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes( + sender_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"] + )))) + content = bytes(sd["encapContentInfo"]["eContent"]) + self.assertSequenceEqual(digest, GOST34112012256(content).digest()) + self.assertTrue(gost3410.verify( + curve, + sender_pub, + GOST34112012256( + SignedAttributes(si["signedAttrs"]).encode() + ).digest()[::-1], + bytes(si["signature"]), + )) + + outer_safe_contents = SafeContents().decod(content) + + safe_bag = outer_safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_data) + safe_contents = OctetStringSafeContents().decod(bytes(safe_bag["bagValue"])) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag) + cert_bag = CertBag().decod(bytes(safe_bag["bagValue"])) + self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate) + _, cert = cert_bag["certValue"].defined + self.assertEqual(Certificate(cert), self.cert_test) + + safe_bag = outer_safe_contents[1] + self.assertEqual(safe_bag["bagId"], id_envelopedData) + ed = EnvelopedData().decod(bytes(safe_bag["bagValue"])) + kari = ed["recipientInfos"][0]["kari"] + ukm = bytes(kari["ukm"]) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_wrap_kexp15, + ) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_256, + ) + kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"]) + keymat = keg(curve, recipient_prv, sender_pub, ukm) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Kuznechik(kek).encrypt, + GOST3412Kuznechik(kim).encrypt, + GOST3412Kuznechik.blocksize, + kexp, + ukm[24:24 + GOST3412Kuznechik.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_ctracpkm, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + content = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(cek).encrypt, + 256 * 1024, + GOST3412Kuznechik.blocksize, + bytes(eci["encryptedContent"]), + eci_ukm[:GOST3412Kuznechik.blocksize // 2], + ) + + safe_contents = SafeContents().decod(content) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_keyBag) + KeyBag().decod(bytes(safe_bag["bagValue"])) + self.assertSequenceEqual(bytes(safe_bag["bagValue"]), self.prv_test_raw) diff --git a/pygost-5.13/build/lib/pygost/test_wrap.py b/pygost-5.13/build/lib/pygost/test_wrap.py new file mode 100644 index 0000000..7ecf8ee --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_wrap.py @@ -0,0 +1,111 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from unittest import TestCase + +from pygost.gost28147 import DEFAULT_SBOX +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.utils import hexdec +from pygost.wrap import kexp15 +from pygost.wrap import kimp15 +from pygost.wrap import unwrap_cryptopro +from pygost.wrap import unwrap_gost +from pygost.wrap import wrap_cryptopro +from pygost.wrap import wrap_gost + + +class WrapGostTest(TestCase): + def test_symmetric(self): + for sbox in (DEFAULT_SBOX, "id-tc26-gost-28147-param-Z"): + for _ in range(1 << 8): + kek = urandom(32) + cek = urandom(32) + ukm = urandom(8) + wrapped = wrap_gost(ukm, kek, cek, sbox=sbox) + unwrapped = unwrap_gost(kek, wrapped, sbox=sbox) + self.assertSequenceEqual(unwrapped, cek) + + def test_invalid_length(self): + with self.assertRaises(ValueError): + unwrap_gost(urandom(32), urandom(41)) + with self.assertRaises(ValueError): + unwrap_gost(urandom(32), urandom(45)) + + +class WrapCryptoproTest(TestCase): + def test_symmetric(self): + for sbox in (DEFAULT_SBOX, "id-tc26-gost-28147-param-Z"): + for _ in range(1 << 8): + kek = urandom(32) + cek = urandom(32) + ukm = urandom(8) + wrapped = wrap_cryptopro(ukm, kek, cek, sbox=sbox) + unwrapped = unwrap_cryptopro(kek, wrapped, sbox=sbox) + self.assertSequenceEqual(unwrapped, cek) + + +class TestVectorKExp15(TestCase): + """Test vectors from Р 1323565.1.017-2018 + """ + key = hexdec("8899AABBCCDDEEFF0011223344556677FEDCBA98765432100123456789ABCDEF") + key_enc = hexdec("202122232425262728292A2B2C2D2E2F38393A3B3C3D3E3F3031323334353637") + key_mac = hexdec("08090A0B0C0D0E0F0001020304050607101112131415161718191A1B1C1D1E1F") + + def test_magma(self): + iv = hexdec("67BED654") + kexp = kexp15( + GOST3412Magma(self.key_enc).encrypt, + GOST3412Magma(self.key_mac).encrypt, + GOST3412Magma.blocksize, + self.key, + iv, + ) + self.assertSequenceEqual(kexp, hexdec(""" +CF D5 A1 2D 5B 81 B6 E1 E9 9C 91 6D 07 90 0C 6A +C1 27 03 FB 3A BD ED 55 56 7B F3 74 2C 89 9C 75 +5D AF E7 B4 2E 3A 8B D9 + """.replace("\n", "").replace(" ", ""))) + self.assertSequenceEqual(kimp15( + GOST3412Magma(self.key_enc).encrypt, + GOST3412Magma(self.key_mac).encrypt, + GOST3412Magma.blocksize, + kexp, + iv, + ), self.key) + + def test_kuznechik(self): + iv = hexdec("0909472DD9F26BE8") + kexp = kexp15( + GOST3412Kuznechik(self.key_enc).encrypt, + GOST3412Kuznechik(self.key_mac).encrypt, + GOST3412Kuznechik.blocksize, + self.key, + iv, + ) + self.assertSequenceEqual(kexp, hexdec(""" +E3 61 84 E8 4E 8D 73 6F F3 6C C2 E5 AE 06 5D C6 +56 B2 3C 20 F5 49 B0 2F DF F8 8E 1F 3F 30 D8 C2 +9A 53 F3 CA 55 4D BA D8 0D E1 52 B9 A4 62 5B 32 + """.replace("\n", "").replace(" ", ""))) + self.assertSequenceEqual(kimp15( + GOST3412Kuznechik(self.key_enc).encrypt, + GOST3412Kuznechik(self.key_mac).encrypt, + GOST3412Kuznechik.blocksize, + kexp, + iv, + ), self.key) diff --git a/pygost-5.13/build/lib/pygost/test_x509.py b/pygost-5.13/build/lib/pygost/test_x509.py new file mode 100644 index 0000000..e9fccb7 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/test_x509.py @@ -0,0 +1,433 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from base64 import b64decode +from unittest import skipIf +from unittest import TestCase + +from pygost.gost3410 import CURVES +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import pub_marshal +from pygost.gost3410 import pub_unmarshal +from pygost.gost3410 import public_key +from pygost.gost3410 import verify +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.utils import hexdec + +try: + + from pyderasn import Any + from pyderasn import BitString + from pyderasn import Boolean + from pyderasn import GeneralizedTime + from pyderasn import Integer + from pyderasn import OctetString + from pyderasn import PrintableString + from pyderasn import UTCTime + + from pygost.asn1schemas.oids import id_at_commonName + from pygost.asn1schemas.oids import id_ce_basicConstraints + from pygost.asn1schemas.oids import id_GostR3410_2001_TestParamSet + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetTest + from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256 + from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512 + from pygost.asn1schemas.pkcs10 import Attributes + from pygost.asn1schemas.pkcs10 import CertificationRequest + from pygost.asn1schemas.pkcs10 import CertificationRequestInfo + from pygost.asn1schemas.x509 import AlgorithmIdentifier + from pygost.asn1schemas.x509 import AttributeType + from pygost.asn1schemas.x509 import AttributeTypeAndValue + from pygost.asn1schemas.x509 import AttributeValue + from pygost.asn1schemas.x509 import BasicConstraints + from pygost.asn1schemas.x509 import Certificate + from pygost.asn1schemas.x509 import CertificateList + from pygost.asn1schemas.x509 import CertificateSerialNumber + from pygost.asn1schemas.x509 import Extension + from pygost.asn1schemas.x509 import Extensions + from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters + from pygost.asn1schemas.x509 import Name + from pygost.asn1schemas.x509 import RDNSequence + from pygost.asn1schemas.x509 import RelativeDistinguishedName + from pygost.asn1schemas.x509 import SubjectPublicKeyInfo + from pygost.asn1schemas.x509 import TBSCertificate + from pygost.asn1schemas.x509 import TBSCertList + from pygost.asn1schemas.x509 import Time + from pygost.asn1schemas.x509 import Validity + from pygost.asn1schemas.x509 import Version + +except ImportError: + pyderasn_exists = False +else: + pyderasn_exists = True + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestCertificate(TestCase): + """Certificate test vectors from "Использования алгоритмов ГОСТ Р + 34.10, ГОСТ Р 34.11 в профиле сертификата и списке отзыва + сертификатов (CRL) инфраструктуры открытых ключей X.509" + (TK26IOK.pdf) + """ + + def process_cert(self, curve_name, hasher, prv_key_raw, cert_raw): + cert, tail = Certificate().decode(cert_raw, ctx={ + "defines_by_path": ( + ( + ( + "tbsCertificate", + "subjectPublicKeyInfo", + "algorithm", + "algorithm", + ), + ( + ( + ("..", "subjectPublicKey"), + { + id_tc26_gost3410_2012_256: OctetString(), + id_tc26_gost3410_2012_512: OctetString(), + }, + ), + ), + ), + ), + }) + self.assertSequenceEqual(tail, b"") + curve = CURVES[curve_name] + prv_key = prv_unmarshal(prv_key_raw) + spk = cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"] + self.assertIsNotNone(spk.defined) + _, pub_key_raw = spk.defined + pub_key = pub_unmarshal(bytes(pub_key_raw)) + self.assertSequenceEqual(pub_key, public_key(curve, prv_key)) + self.assertTrue(verify( + curve, + pub_key, + hasher(cert["tbsCertificate"].encode()).digest()[::-1], + bytes(cert["signatureValue"]), + )) + + def test_256(self): + cert_raw = b64decode(""" +MIICYjCCAg+gAwIBAgIBATAKBggqhQMHAQEDAjBWMSkwJwYJKoZIhvcNAQkBFhpH +b3N0UjM0MTAtMjAxMkBleGFtcGxlLmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIw +MTIgKDI1NiBiaXQpIGV4YW1wbGUwHhcNMTMxMTA1MTQwMjM3WhcNMzAxMTAxMTQw +MjM3WjBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxlLmNv +bTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgKDI1NiBiaXQpIGV4YW1wbGUwZjAf +BggqhQMHAQEBATATBgcqhQMCAiQABggqhQMHAQECAgNDAARAut/Qw1MUq9KPqkdH +C2xAF3K7TugHfo9n525D2s5mFZdD5pwf90/i4vF0mFmr9nfRwMYP4o0Pg1mOn5Rl +aXNYraOBwDCBvTAdBgNVHQ4EFgQU1fIeN1HaPbw+XWUzbkJ+kHJUT0AwCwYDVR0P +BAQDAgHGMA8GA1UdEwQIMAYBAf8CAQEwfgYDVR0BBHcwdYAU1fIeN1HaPbw+XWUz +bkJ+kHJUT0ChWqRYMFYxKTAnBgkqhkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4 +YW1wbGUuY29tMSkwJwYDVQQDEyBHb3N0UjM0MTAtMjAxMiAoMjU2IGJpdCkgZXhh +bXBsZYIBATAKBggqhQMHAQEDAgNBAF5bm4BbARR6hJLEoWJkOsYV3Hd7kXQQjz3C +dqQfmHrz6TI6Xojdh/t8ckODv/587NS5/6KsM77vc6Wh90NAT2s= + """) + prv_key_raw = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1] + self.process_cert( + "id-GostR3410-2001-CryptoPro-XchA-ParamSet", + GOST34112012256, + prv_key_raw, + cert_raw, + ) + + def test_512(self): + cert_raw = b64decode(""" +MIIC6DCCAlSgAwIBAgIBATAKBggqhQMHAQEDAzBWMSkwJwYJKoZIhvcNAQkBFhpH +b3N0UjM0MTAtMjAxMkBleGFtcGxlLmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIw +MTIgKDUxMiBiaXQpIGV4YW1wbGUwHhcNMTMxMDA0MDczNjA0WhcNMzAxMDAxMDcz +NjA0WjBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxlLmNv +bTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgKDUxMiBiaXQpIGV4YW1wbGUwgaow +IQYIKoUDBwEBAQIwFQYJKoUDBwECAQICBggqhQMHAQECAwOBhAAEgYATGQ9VCiM5 +FRGCQ8MEz2F1dANqhaEuywa8CbxOnTvaGJpFQVXQwkwvLFAKh7hk542vOEtxpKtT +CXfGf84nRhMH/Q9bZeAc2eO/yhxrsQhTBufa1Fuou2oe/jUOaG6RAtUUvRzhNTpp +RGGl1+EIY2vzzUua9j9Ol/gAoy/LNKQIfqOBwDCBvTAdBgNVHQ4EFgQUPcbTRXJZ +nHtjj+eBP7b5lcTMekIwCwYDVR0PBAQDAgHGMA8GA1UdEwQIMAYBAf8CAQEwfgYD +VR0BBHcwdYAUPcbTRXJZnHtjj+eBP7b5lcTMekKhWqRYMFYxKTAnBgkqhkiG9w0B +CQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBHb3N0UjM0 +MTAtMjAxMiAoNTEyIGJpdCkgZXhhbXBsZYIBATAKBggqhQMHAQEDAwOBgQBObS7o +ppPTXzHyVR1DtPa8b57nudJzI4czhsfeX5HDntOq45t9B/qSs8dC6eGxbhHZ9zCO +SFtxWYdmg0au8XI9Xb8vTC1qdwWID7FFjMWDNQZb6lYh/J+8F2xKylvB5nIlRZqO +o3eUNFkNyHJwQCk2WoOlO16zwGk2tdKH4KmD5w== + """) + prv_key_raw = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1] + self.process_cert( + "id-tc26-gost-3410-12-512-paramSetB", + GOST34112012512, + prv_key_raw, + cert_raw, + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestRFC4491bis(TestCase): + """Test vectors from https://tools.ietf.org/html/draft-deremin-rfc4491-bis-02 + """ + + def _test_vector( + self, + curve_name, + hsh, + ai_spki, + ai_sign, + cert_serial, + prv_hex, + cr_sign_hex, + cr_b64, + c_sign_hex, + c_b64, + crl_sign_hex, + crl_b64, + ): + prv_raw = hexdec(prv_hex)[::-1] + prv = prv_unmarshal(prv_raw) + curve = CURVES[curve_name] + pub = public_key(curve, prv) + pub_raw = pub_marshal(pub) + subj = Name(("rdnSequence", RDNSequence([ + RelativeDistinguishedName(( + AttributeTypeAndValue(( + ("type", AttributeType(id_at_commonName)), + ("value", AttributeValue(PrintableString("Example"))), + )), + )) + ]))) + spki = SubjectPublicKeyInfo(( + ("algorithm", ai_spki), + ("subjectPublicKey", BitString(OctetString(pub_raw).encode())), + )) + + # Certification request + cri = CertificationRequestInfo(( + ("version", Integer(0)), + ("subject", subj), + ("subjectPKInfo", spki), + ("attributes", Attributes()), + )) + sign = hexdec(cr_sign_hex) + self.assertTrue(verify( + curve, + pub, + hsh(cri.encode()).digest()[::-1], + sign, + )) + cr = CertificationRequest(( + ("certificationRequestInfo", cri), + ("signatureAlgorithm", ai_sign), + ("signature", BitString(sign)), + )) + self.assertSequenceEqual(cr.encode(), b64decode(cr_b64)) + + # Certificate + tbs = TBSCertificate(( + ("version", Version("v3")), + ("serialNumber", CertificateSerialNumber(cert_serial)), + ("signature", ai_sign), + ("issuer", subj), + ("validity", Validity(( + ("notBefore", Time(("utcTime", UTCTime(b"010101000000Z")))), + ("notAfter", Time(("generalTime", GeneralizedTime(b"20501231000000Z")))), + ))), + ("subject", subj), + ("subjectPublicKeyInfo", spki), + ("extensions", Extensions(( + Extension(( + ("extnID", id_ce_basicConstraints), + ("critical", Boolean(True)), + ("extnValue", OctetString( + BasicConstraints((("cA", Boolean(True)),)).encode() + )), + )), + ))), + )) + sign = hexdec(c_sign_hex) + self.assertTrue(verify( + curve, + pub, + hsh(tbs.encode()).digest()[::-1], + sign, + )) + cert = Certificate(( + ("tbsCertificate", tbs), + ("signatureAlgorithm", ai_sign), + ("signatureValue", BitString(sign)), + )) + self.assertSequenceEqual(cert.encode(), b64decode(c_b64)) + + # CRL + tbs = TBSCertList(( + ("version", Version("v2")), + ("signature", ai_sign), + ("issuer", subj), + ("thisUpdate", Time(("utcTime", UTCTime(b"140101000000Z")))), + ("nextUpdate", Time(("utcTime", UTCTime(b"140102000000Z")))), + )) + sign = hexdec(crl_sign_hex) + self.assertTrue(verify( + curve, + pub, + hsh(tbs.encode()).digest()[::-1], + sign, + )) + crl = CertificateList(( + ("tbsCertList", tbs), + ("signatureAlgorithm", ai_sign), + ("signatureValue", BitString(sign)), + )) + self.assertSequenceEqual(crl.encode(), b64decode(crl_b64)) + + def test_256_test_paramset(self): + self._test_vector( + "id-GostR3410-2001-TestParamSet", + GOST34112012256, + AlgorithmIdentifier(( + ("algorithm", id_tc26_gost3410_2012_256), + ("parameters", Any( + GostR34102012PublicKeyParameters(( + ("publicKeyParamSet", id_GostR3410_2001_TestParamSet), + ("digestParamSet", id_tc26_gost3411_2012_256), + )) + )), + )), + AlgorithmIdentifier(( + ("algorithm", id_tc26_signwithdigest_gost3410_2012_256), + )), + 10, + "7A929ADE789BB9BE10ED359DD39A72C11B60961F49397EEE1D19CE9891EC3B28", + "6AAAB38E35D4AAA517940301799122D855484F579F4CBB96D63CDFDF3ACC432A41AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493", + """ +MIHTMIGBAgEAMBIxEDAOBgNVBAMTB0V4YW1wbGUwZjAfBggqhQMHAQEBATATBgcq +hQMCAiMABggqhQMHAQECAgNDAARAC9hv5djbiWaPeJtOHbqFhcVQi0XsW1nYkG3b +cOJJK3/ad/+HGhD73ydm0pPF0WSvuzx7lzpByIXRHXDWibTxJqAAMAoGCCqFAwcB +AQMCA0EAaqqzjjXUqqUXlAMBeZEi2FVIT1efTLuW1jzf3zrMQypBqijS8asUgoDN +ntVv7aQZdAU1VKQnZ7g60EP9OdwEkw== + """, + "4D53F012FE081776507D4D9BB81F00EFDB4EEFD4AB83BAC4BACF735173CFA81C41AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493", + """ +MIIBLTCB26ADAgECAgEKMAoGCCqFAwcBAQMCMBIxEDAOBgNVBAMTB0V4YW1wbGUw +IBcNMDEwMTAxMDAwMDAwWhgPMjA1MDEyMzEwMDAwMDBaMBIxEDAOBgNVBAMTB0V4 +YW1wbGUwZjAfBggqhQMHAQEBATATBgcqhQMCAiMABggqhQMHAQECAgNDAARAC9hv +5djbiWaPeJtOHbqFhcVQi0XsW1nYkG3bcOJJK3/ad/+HGhD73ydm0pPF0WSvuzx7 +lzpByIXRHXDWibTxJqMTMBEwDwYDVR0TAQH/BAUwAwEB/zAKBggqhQMHAQEDAgNB +AE1T8BL+CBd2UH1Nm7gfAO/bTu/Uq4O6xLrPc1Fzz6gcQaoo0vGrFIKAzZ7Vb+2k +GXQFNVSkJ2e4OtBD/TncBJM= + """, + "42BF392A14D3EBE957AF3E46CB50BF5F4221A003AD3D172753C94A9C37A31D2041AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493", + """ +MIGSMEECAQEwCgYIKoUDBwEBAwIwEjEQMA4GA1UEAxMHRXhhbXBsZRcNMTQwMTAx +MDAwMDAwWhcNMTQwMTAyMDAwMDAwWjAKBggqhQMHAQEDAgNBAEK/OSoU0+vpV68+ +RstQv19CIaADrT0XJ1PJSpw3ox0gQaoo0vGrFIKAzZ7Vb+2kGXQFNVSkJ2e4OtBD +/TncBJM= + """, + ) + + def test_256a_paramset(self): + self._test_vector( + "id-tc26-gost-3410-2012-256-paramSetA", + GOST34112012256, + AlgorithmIdentifier(( + ("algorithm", id_tc26_gost3410_2012_256), + ("parameters", Any( + GostR34102012PublicKeyParameters(( + ("publicKeyParamSet", id_tc26_gost3410_2012_256_paramSetA), + )) + )), + )), + AlgorithmIdentifier(( + ("algorithm", id_tc26_signwithdigest_gost3410_2012_256), + )), + 10, + "7A929ADE789BB9BE10ED359DD39A72C11B60961F49397EEE1D19CE9891EC3B28", + "1BDC2A1317679B66232F63EA16FF7C64CCAAB9AD855FC6E18091661DB79D48121D0E1DA5BE347C6F1B5256C7AEAC200AD64AC77A6F5B3A0E097318E7AE6EE769", + """ +MIHKMHkCAQAwEjEQMA4GA1UEAxMHRXhhbXBsZTBeMBcGCCqFAwcBAQEBMAsGCSqF +AwcBAgEBAQNDAARAdCeV1L7ohN3yhQ/sA+o/rxhE4B2dpgtkUJOlXibfw5l49ZbP +TU0MbPHRiUPZRJPRa57AoW1RLS4SfMRpGmMY4qAAMAoGCCqFAwcBAQMCA0EAG9wq +Exdnm2YjL2PqFv98ZMyqua2FX8bhgJFmHbedSBIdDh2lvjR8bxtSVseurCAK1krH +em9bOg4Jcxjnrm7naQ== + """, + "140B4DA9124B09CB0D5CE928EE874273A310129492EC0E29369E3B791248578C1D0E1DA5BE347C6F1B5256C7AEAC200AD64AC77A6F5B3A0E097318E7AE6EE769", + """ +MIIBJTCB06ADAgECAgEKMAoGCCqFAwcBAQMCMBIxEDAOBgNVBAMTB0V4YW1wbGUw +IBcNMDEwMTAxMDAwMDAwWhgPMjA1MDEyMzEwMDAwMDBaMBIxEDAOBgNVBAMTB0V4 +YW1wbGUwXjAXBggqhQMHAQEBATALBgkqhQMHAQIBAQEDQwAEQHQnldS+6ITd8oUP +7APqP68YROAdnaYLZFCTpV4m38OZePWWz01NDGzx0YlD2UST0WuewKFtUS0uEnzE +aRpjGOKjEzARMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoUDBwEBAwIDQQAUC02pEksJ +yw1c6Sjuh0JzoxASlJLsDik2njt5EkhXjB0OHaW+NHxvG1JWx66sIArWSsd6b1s6 +DglzGOeubudp + """, + "14BD68087C3B903C7AA28B07FEB2E7BD6FE0963F563267359F5CD8EAB45059AD1D0E1DA5BE347C6F1B5256C7AEAC200AD64AC77A6F5B3A0E097318E7AE6EE769", + """ +MIGSMEECAQEwCgYIKoUDBwEBAwIwEjEQMA4GA1UEAxMHRXhhbXBsZRcNMTQwMTAx +MDAwMDAwWhcNMTQwMTAyMDAwMDAwWjAKBggqhQMHAQEDAgNBABS9aAh8O5A8eqKL +B/6y571v4JY/VjJnNZ9c2Oq0UFmtHQ4dpb40fG8bUlbHrqwgCtZKx3pvWzoOCXMY +565u52k= + """, + ) + + def test_512_test_paramset(self): + self._test_vector( + "id-tc26-gost-3410-2012-512-paramSetTest", + GOST34112012512, + AlgorithmIdentifier(( + ("algorithm", id_tc26_gost3410_2012_512), + ("parameters", Any( + GostR34102012PublicKeyParameters(( + ("publicKeyParamSet", id_tc26_gost3410_2012_512_paramSetTest), + )) + )), + )), + AlgorithmIdentifier(( + ("algorithm", id_tc26_signwithdigest_gost3410_2012_512), + )), + 11, + "0BA6048AADAE241BA40936D47756D7C93091A0E8514669700EE7508E508B102072E8123B2200A0563322DAD2827E2714A2636B7BFD18AADFC62967821FA18DD4", + "433B1D6CE40A51F1E5737EB16AA2C683829A405B9D9127E21260FC9D6AC05D87BF24E26C45278A5C2192A75BA94993ABD6074E7FF1BF03FD2F5397AFA1D945582F86FA60A081091A23DD795E1E3C689EE512A3C82EE0DCC2643C78EEA8FCACD35492558486B20F1C9EC197C90699850260C93BCBCD9C5C3317E19344E173AE36", + """ +MIIBTzCBvAIBADASMRAwDgYDVQQDEwdFeGFtcGxlMIGgMBcGCCqFAwcBAQECMAsG +CSqFAwcBAgECAAOBhAAEgYDh7zDVLGEz3dmdHVxBRVz3302LTJJbvGmvFDPRVlhR +Wt0hRoUMMlxbgcEzvmVaqMTUQOe5io1ZSHsMdpa8xV0R7L53NqnsNX/y/TmTH04R +TLjNo1knCsfw5/9D2UGUGeph/Sq3f12fY1I9O1CgT2PioM9Rt8E63CFWDwvUDMnH +N6AAMAoGCCqFAwcBAQMDA4GBAEM7HWzkClHx5XN+sWqixoOCmkBbnZEn4hJg/J1q +wF2HvyTibEUnilwhkqdbqUmTq9YHTn/xvwP9L1OXr6HZRVgvhvpgoIEJGiPdeV4e +PGie5RKjyC7g3MJkPHjuqPys01SSVYSGsg8cnsGXyQaZhQJgyTvLzZxcMxfhk0Th +c642 + """, + "415703D892F1A5F3F68C4353189A7EE207B80B5631EF9D49529A4D6B542C2CFA15AA2EACF11F470FDE7D954856903C35FD8F955EF300D95C77534A724A0EEE702F86FA60A081091A23DD795E1E3C689EE512A3C82EE0DCC2643C78EEA8FCACD35492558486B20F1C9EC197C90699850260C93BCBCD9C5C3317E19344E173AE36", + """ +MIIBqjCCARagAwIBAgIBCzAKBggqhQMHAQEDAzASMRAwDgYDVQQDEwdFeGFtcGxl +MCAXDTAxMDEwMTAwMDAwMFoYDzIwNTAxMjMxMDAwMDAwWjASMRAwDgYDVQQDEwdF +eGFtcGxlMIGgMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAAOBhAAEgYDh7zDVLGEz +3dmdHVxBRVz3302LTJJbvGmvFDPRVlhRWt0hRoUMMlxbgcEzvmVaqMTUQOe5io1Z +SHsMdpa8xV0R7L53NqnsNX/y/TmTH04RTLjNo1knCsfw5/9D2UGUGeph/Sq3f12f +Y1I9O1CgT2PioM9Rt8E63CFWDwvUDMnHN6MTMBEwDwYDVR0TAQH/BAUwAwEB/zAK +BggqhQMHAQEDAwOBgQBBVwPYkvGl8/aMQ1MYmn7iB7gLVjHvnUlSmk1rVCws+hWq +LqzxH0cP3n2VSFaQPDX9j5Ve8wDZXHdTSnJKDu5wL4b6YKCBCRoj3XleHjxonuUS +o8gu4NzCZDx47qj8rNNUklWEhrIPHJ7Bl8kGmYUCYMk7y82cXDMX4ZNE4XOuNg== + """, + "3A13FB7AECDB5560EEF6137CFC5DD64691732EBFB3690A1FC0C7E8A4EEEA08307D648D4DC0986C46A87B3FBE4C7AF42EA34359C795954CA39FF3ABBED9051F4D2F86FA60A081091A23DD795E1E3C689EE512A3C82EE0DCC2643C78EEA8FCACD35492558486B20F1C9EC197C90699850260C93BCBCD9C5C3317E19344E173AE36", + """ +MIHTMEECAQEwCgYIKoUDBwEBAwMwEjEQMA4GA1UEAxMHRXhhbXBsZRcNMTQwMTAx +MDAwMDAwWhcNMTQwMTAyMDAwMDAwWjAKBggqhQMHAQEDAwOBgQA6E/t67NtVYO72 +E3z8XdZGkXMuv7NpCh/Ax+ik7uoIMH1kjU3AmGxGqHs/vkx69C6jQ1nHlZVMo5/z +q77ZBR9NL4b6YKCBCRoj3XleHjxonuUSo8gu4NzCZDx47qj8rNNUklWEhrIPHJ7B +l8kGmYUCYMk7y82cXDMX4ZNE4XOuNg== + """, + ) diff --git a/pygost-5.13/build/lib/pygost/utils.py b/pygost-5.13/build/lib/pygost/utils.py new file mode 100644 index 0000000..21c31b0 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/utils.py @@ -0,0 +1,101 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from codecs import getdecoder +from codecs import getencoder +from sys import version_info + + +xrange = range if version_info[0] == 3 else xrange + + +def strxor(a, b): + """XOR of two strings + + This function will process only shortest length of both strings, + ignoring remaining one. + """ + mlen = min(len(a), len(b)) + a, b, xor = bytearray(a), bytearray(b), bytearray(mlen) + for i in xrange(mlen): + xor[i] = a[i] ^ b[i] + return bytes(xor) + + +_hexdecoder = getdecoder("hex") +_hexencoder = getencoder("hex") + + +def hexdec(data): + """Decode hexadecimal + """ + return _hexdecoder(data)[0] + + +def hexenc(data): + """Encode hexadecimal + """ + return _hexencoder(data)[0].decode("ascii") + + +def bytes2long(raw): + """Deserialize big-endian bytes into long number + + :param bytes raw: binary string + :returns: deserialized long number + :rtype: int + """ + return int(hexenc(raw), 16) + + +def long2bytes(n, size=32): + """Serialize long number into big-endian bytestring + + :param long n: long number + :returns: serialized bytestring + :rtype: bytes + """ + res = hex(int(n))[2:].rstrip("L") + if len(res) % 2 != 0: + res = "0" + res + s = hexdec(res) + if len(s) != size: + s = (size - len(s)) * b"\x00" + s + return s + + +def modinvert(a, n): + """Modular multiplicative inverse + + :returns: inverse number. -1 if it does not exist + + Realization is taken from: + https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm + """ + if a < 0: + # k^-1 = p - (-k)^-1 mod p + return n - modinvert(-a, n) + t, newt = 0, 1 + r, newr = n, a + while newr != 0: + quotinent = r // newr + t, newt = newt, t - quotinent * newt + r, newr = newr, r - quotinent * newr + if r > 1: + return -1 + if t < 0: + t = t + n + return t diff --git a/pygost-5.13/build/lib/pygost/wrap.py b/pygost-5.13/build/lib/pygost/wrap.py new file mode 100644 index 0000000..3fa49e7 --- /dev/null +++ b/pygost-5.13/build/lib/pygost/wrap.py @@ -0,0 +1,152 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Key wrap. + +:rfc:`4357` key wrapping (28147-89 and CryptoPro). +""" + +from hmac import compare_digest +from struct import pack +from struct import unpack + +from pygost.gost28147 import cfb_encrypt +from pygost.gost28147 import DEFAULT_SBOX +from pygost.gost28147 import ecb_decrypt +from pygost.gost28147 import ecb_encrypt +from pygost.gost28147_mac import MAC +from pygost.gost3413 import ctr +from pygost.gost3413 import mac + + +def wrap_gost(ukm, kek, cek, sbox=DEFAULT_SBOX): + """28147-89 key wrapping + + :param ukm: UKM + :type ukm: bytes, 8 bytes + :param kek: key encryption key + :type kek: bytes, 32 bytes + :param cek: content encryption key + :type cek: bytes, 32 bytes + :returns: wrapped key + :rtype: bytes, 44 bytes + """ + cek_mac = MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4] + cek_enc = ecb_encrypt(kek, cek, sbox=sbox) + return ukm + cek_enc + cek_mac + + +def unwrap_gost(kek, data, sbox=DEFAULT_SBOX): + """28147-89 key unwrapping + + :param kek: key encryption key + :type kek: bytes, 32 bytes + :param data: wrapped key + :type data: bytes, 44 bytes + :returns: unwrapped CEK + :rtype: 32 bytes + """ + if len(data) != 44: + raise ValueError("Invalid data length") + ukm, cek_enc, cek_mac = data[:8], data[8:8 + 32], data[-4:] + cek = ecb_decrypt(kek, cek_enc, sbox=sbox) + if MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4] != cek_mac: + raise ValueError("Invalid MAC") + return cek + + +def wrap_cryptopro(ukm, kek, cek, sbox=DEFAULT_SBOX): + """CryptoPro key wrapping + + :param ukm: UKM + :type ukm: bytes, 8 bytes + :param kek: key encryption key + :type kek: bytes, 32 bytes + :param cek: content encryption key + :type cek: bytes, 32 bytes + :returns: wrapped key + :rtype: bytes, 44 bytes + """ + return wrap_gost( + ukm, + diversify(kek, bytearray(ukm), sbox=sbox), + cek, + sbox=sbox, + ) + + +def unwrap_cryptopro(kek, data, sbox=DEFAULT_SBOX): + """CryptoPro key unwrapping + + :param kek: key encryption key + :type kek: bytes, 32 bytes + :param data: wrapped key + :type data: bytes, 44 bytes + :returns: unwrapped CEK + :rtype: 32 bytes + """ + if len(data) < 8: + raise ValueError("Invalid data length") + return unwrap_gost( + diversify(kek, bytearray(data[:8]), sbox=sbox), + data, + sbox=sbox, + ) + + +def diversify(kek, ukm, sbox=DEFAULT_SBOX): + out = kek + for i in range(8): + s1, s2 = 0, 0 + for j in range(8): + k, = unpack("> j) & 1: + s1 += k + else: + s2 += k + iv = pack(">> from pygost.gost3410 import CURVES + >>> curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + >>> from os import urandom + >>> prv_raw = urandom(64) + >>> from pygost.gost3410 import prv_unmarshal + >>> from pygost.gost3410 import prv_marshal + >>> prv = prv_unmarshal(prv_raw) + >>> prv_raw = prv_marshal(curve, prv) + >>> from pygost.gost3410 import public_key + >>> pub = public_key(curve, prv) + >>> from pygost.gost3410 import pub_marshal + >>> from pygost.utils import hexenc + >>> print "Public key is:", hexenc(pub_marshal(pub)) + >>> from pygost import gost34112012512 + >>> data_for_signing = b"some data" + >>> dgst = gost34112012512.new(data_for_signing).digest() + >>> from pygost.gost3410 import sign + >>> signature = sign(curve, prv, dgst) + >>> from pygost.gost3410 import verify + >>> verify(curve, pub, dgst, signature) + True + +Other examples can be found in docstrings and unittests. +Example self-signed X.509 certificate creation can be found in +pygost/asn1schemas/cert-selfsigned-example.py. + +PyGOST is free software: see the file COPYING for copying conditions. + +PyGOST'es home page is: http://www.pygost.cypherpunks.ru/ +You can read about GOST algorithms more: http://www.gost.cypherpunks.ru/ + +Please send questions, bug reports and patches to +http://lists.cypherpunks.ru/gost.html mailing list. +Announcements also go to this mailing list. + +Development Git source code repository currently is located here: +http://www.git.cypherpunks.ru/?p=pygost.git;a=summary diff --git a/pygost-5.13/pygost.egg-info/SOURCES.txt b/pygost-5.13/pygost.egg-info/SOURCES.txt new file mode 100644 index 0000000..781d116 --- /dev/null +++ b/pygost-5.13/pygost.egg-info/SOURCES.txt @@ -0,0 +1,71 @@ +AUTHORS +COPYING +FAQ +INSTALL +MANIFEST.in +NEWS +README +THANKS +VERSION +setup.cfg +setup.py +pygost/__init__.py +pygost/gost28147.py +pygost/gost28147_mac.py +pygost/gost3410.py +pygost/gost3410_vko.py +pygost/gost34112012.py +pygost/gost34112012256.py +pygost/gost34112012512.py +pygost/gost341194.py +pygost/gost3412.py +pygost/gost3413.py +pygost/iface.py +pygost/kdf.py +pygost/mgm.py +pygost/pbkdf2.py +pygost/test_cms.py +pygost/test_gost28147.py +pygost/test_gost28147_mac.py +pygost/test_gost3410.py +pygost/test_gost3410_vko.py +pygost/test_gost34112012.py +pygost/test_gost341194.py +pygost/test_gost3412.py +pygost/test_gost3413.py +pygost/test_kdf.py +pygost/test_mgm.py +pygost/test_pfx.py +pygost/test_wrap.py +pygost/test_x509.py +pygost/utils.py +pygost/wrap.py +pygost.egg-info/PKG-INFO +pygost.egg-info/SOURCES.txt +pygost.egg-info/dependency_links.txt +pygost.egg-info/top_level.txt +pygost/asn1schemas/__init__.py +pygost/asn1schemas/cert-dane-hash.py +pygost/asn1schemas/cert-selfsigned-example.py +pygost/asn1schemas/cms.py +pygost/asn1schemas/oids.py +pygost/asn1schemas/pfx.py +pygost/asn1schemas/pkcs10.py +pygost/asn1schemas/prvkey.py +pygost/asn1schemas/x509.py +pygost/stubs/pygost/__init__.pyi +pygost/stubs/pygost/gost28147.pyi +pygost/stubs/pygost/gost28147_mac.pyi +pygost/stubs/pygost/gost3410.pyi +pygost/stubs/pygost/gost3410_vko.pyi +pygost/stubs/pygost/gost34112012.pyi +pygost/stubs/pygost/gost34112012256.pyi +pygost/stubs/pygost/gost34112012512.pyi +pygost/stubs/pygost/gost341194.pyi +pygost/stubs/pygost/gost3412.pyi +pygost/stubs/pygost/gost3413.pyi +pygost/stubs/pygost/iface.pyi +pygost/stubs/pygost/kdf.pyi +pygost/stubs/pygost/mgm.pyi +pygost/stubs/pygost/utils.pyi +pygost/stubs/pygost/wrap.pyi \ No newline at end of file diff --git a/pygost-5.13/pygost.egg-info/dependency_links.txt b/pygost-5.13/pygost.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pygost-5.13/pygost.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pygost-5.13/pygost.egg-info/top_level.txt b/pygost-5.13/pygost.egg-info/top_level.txt new file mode 100644 index 0000000..d05bec6 --- /dev/null +++ b/pygost-5.13/pygost.egg-info/top_level.txt @@ -0,0 +1 @@ +pygost diff --git a/pygost-5.13/pygost/__init__.py b/pygost-5.13/pygost/__init__.py new file mode 100644 index 0000000..fba7932 --- /dev/null +++ b/pygost-5.13/pygost/__init__.py @@ -0,0 +1,6 @@ +"""Pure Python GOST cryptographic functions library. + +PyGOST is free software: see the file COPYING for copying conditions. +""" + +__version__ = "5.13" diff --git a/pygost-5.13/pygost/asn1schemas/__init__.py b/pygost-5.13/pygost/asn1schemas/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pygost-5.13/pygost/asn1schemas/cert-dane-hash.py b/pygost-5.13/pygost/asn1schemas/cert-dane-hash.py new file mode 100755 index 0000000..0292b9e --- /dev/null +++ b/pygost-5.13/pygost/asn1schemas/cert-dane-hash.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +"""DANE's SPKI hash calculator +""" + +from base64 import standard_b64decode +from hashlib import sha256 +import sys + +from pygost.asn1schemas.x509 import Certificate + + +lines = sys.stdin.read().split("-----") +idx = lines.index("BEGIN CERTIFICATE") +if idx == -1: + raise ValueError("PEM has no CERTIFICATE") +cert_raw = standard_b64decode(lines[idx + 1]) +cert = Certificate().decod(cert_raw) +print(sha256(cert["tbsCertificate"]["subjectPublicKeyInfo"].encode()).hexdigest()) diff --git a/pygost-5.13/pygost/asn1schemas/cert-selfsigned-example.py b/pygost-5.13/pygost/asn1schemas/cert-selfsigned-example.py new file mode 100755 index 0000000..bd562b1 --- /dev/null +++ b/pygost-5.13/pygost/asn1schemas/cert-selfsigned-example.py @@ -0,0 +1,348 @@ +#!/usr/bin/env python3 +"""Create example self-signed X.509 certificate +""" + +from argparse import ArgumentParser +from base64 import standard_b64decode +from base64 import standard_b64encode +from datetime import datetime +from datetime import timedelta +from os import urandom +from sys import exit as sys_exit +from sys import stdout +from textwrap import fill + +from pyderasn import Any +from pyderasn import BitString +from pyderasn import Boolean +from pyderasn import IA5String +from pyderasn import Integer +from pyderasn import OctetString +from pyderasn import PrintableString +from pyderasn import UTCTime + +from pygost.asn1schemas.oids import id_at_commonName +from pygost.asn1schemas.oids import id_at_countryName +from pygost.asn1schemas.oids import id_ce_authorityKeyIdentifier +from pygost.asn1schemas.oids import id_ce_basicConstraints +from pygost.asn1schemas.oids import id_ce_keyUsage +from pygost.asn1schemas.oids import id_ce_subjectAltName +from pygost.asn1schemas.oids import id_ce_subjectKeyIdentifier +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetB +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetC +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetD +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetB +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetC +from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256 +from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512 +from pygost.asn1schemas.prvkey import PrivateKey +from pygost.asn1schemas.prvkey import PrivateKeyAlgorithmIdentifier +from pygost.asn1schemas.prvkey import PrivateKeyInfo +from pygost.asn1schemas.x509 import AlgorithmIdentifier +from pygost.asn1schemas.x509 import AttributeType +from pygost.asn1schemas.x509 import AttributeTypeAndValue +from pygost.asn1schemas.x509 import AttributeValue +from pygost.asn1schemas.x509 import AuthorityKeyIdentifier +from pygost.asn1schemas.x509 import BasicConstraints +from pygost.asn1schemas.x509 import Certificate +from pygost.asn1schemas.x509 import CertificateSerialNumber +from pygost.asn1schemas.x509 import Extension +from pygost.asn1schemas.x509 import Extensions +from pygost.asn1schemas.x509 import GeneralName +from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters +from pygost.asn1schemas.x509 import KeyIdentifier +from pygost.asn1schemas.x509 import KeyUsage +from pygost.asn1schemas.x509 import Name +from pygost.asn1schemas.x509 import RDNSequence +from pygost.asn1schemas.x509 import RelativeDistinguishedName +from pygost.asn1schemas.x509 import SubjectAltName +from pygost.asn1schemas.x509 import SubjectKeyIdentifier +from pygost.asn1schemas.x509 import SubjectPublicKeyInfo +from pygost.asn1schemas.x509 import TBSCertificate +from pygost.asn1schemas.x509 import Time +from pygost.asn1schemas.x509 import Validity +from pygost.asn1schemas.x509 import Version +from pygost.gost3410 import CURVES +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import pub_marshal +from pygost.gost3410 import public_key +from pygost.gost3410 import sign +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.utils import bytes2long + +parser = ArgumentParser(description="Self-signed X.509 certificate creator") +parser.add_argument( + "--ca", + action="store_true", + help="Enable BasicConstraints.cA", +) +parser.add_argument( + "--cn", + required=True, + help="Subject's CommonName", +) +parser.add_argument( + "--country", + help="Subject's Country", +) +parser.add_argument( + "--serial", + help="Serial number", +) +parser.add_argument( + "--ai", + required=True, + help="Signing algorithm: {256[ABCD],512[ABC]}", +) +parser.add_argument( + "--issue-with", + help="Path to PEM with CA to issue the child", +) +parser.add_argument( + "--reuse-key", + help="Path to PEM with the key to reuse", +) +parser.add_argument( + "--out-key", + help="Path to PEM with the resulting key", +) +parser.add_argument( + "--only-key", + action="store_true", + help="Only generate the key", +) +parser.add_argument( + "--out-cert", + help="Path to PEM with the resulting certificate", +) +args = parser.parse_args() +AIs = { + "256A": { + "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetA, + "key_algorithm": id_tc26_gost3410_2012_256, + "prv_len": 32, + "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetA"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256, + "hasher": GOST34112012256, + }, + "256B": { + "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetB, + "key_algorithm": id_tc26_gost3410_2012_256, + "prv_len": 32, + "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetB"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256, + "hasher": GOST34112012256, + }, + "256C": { + "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetC, + "key_algorithm": id_tc26_gost3410_2012_256, + "prv_len": 32, + "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetC"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256, + "hasher": GOST34112012256, + }, + "256D": { + "publicKeyParamSet": id_tc26_gost3410_2012_256_paramSetD, + "key_algorithm": id_tc26_gost3410_2012_256, + "prv_len": 32, + "curve": CURVES["id-tc26-gost-3410-2012-256-paramSetD"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_256, + "hasher": GOST34112012256, + }, + "512A": { + "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetA, + "key_algorithm": id_tc26_gost3410_2012_512, + "prv_len": 64, + "curve": CURVES["id-tc26-gost-3410-12-512-paramSetA"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512, + "hasher": GOST34112012512, + }, + "512B": { + "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetB, + "key_algorithm": id_tc26_gost3410_2012_512, + "prv_len": 64, + "curve": CURVES["id-tc26-gost-3410-12-512-paramSetB"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512, + "hasher": GOST34112012512, + }, + "512C": { + "publicKeyParamSet": id_tc26_gost3410_2012_512_paramSetC, + "key_algorithm": id_tc26_gost3410_2012_512, + "prv_len": 64, + "curve": CURVES["id-tc26-gost-3410-2012-512-paramSetC"], + "sign_algorithm": id_tc26_signwithdigest_gost3410_2012_512, + "hasher": GOST34112012512, + }, +} +ai = AIs[args.ai] + +ca_prv = None +ca_cert = None +ca_subj = None +ca_ai = None +if args.issue_with is not None: + with open(args.issue_with, "rb") as fd: + lines = fd.read().decode("ascii").split("-----") + idx = lines.index("BEGIN PRIVATE KEY") + if idx == -1: + raise ValueError("PEM has no PRIVATE KEY") + prv_raw = standard_b64decode(lines[idx + 1]) + idx = lines.index("BEGIN CERTIFICATE") + if idx == -1: + raise ValueError("PEM has no CERTIFICATE") + cert_raw = standard_b64decode(lines[idx + 1]) + pki = PrivateKeyInfo().decod(prv_raw) + ca_prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"])))) + ca_cert = Certificate().decod(cert_raw) + tbs = ca_cert["tbsCertificate"] + ca_subj = tbs["subject"] + curve_oid = GostR34102012PublicKeyParameters().decod(bytes( + tbs["subjectPublicKeyInfo"]["algorithm"]["parameters"] + ))["publicKeyParamSet"] + ca_ai = next(iter([ + params for params in AIs.values() + if params["publicKeyParamSet"] == curve_oid + ])) + +key_params = GostR34102012PublicKeyParameters(( + ("publicKeyParamSet", ai["publicKeyParamSet"]), +)) + + +def pem(obj): + return fill(standard_b64encode(obj.encode()).decode("ascii"), 64) + + +if args.reuse_key is not None: + with open(args.reuse_key, "rb") as fd: + lines = fd.read().decode("ascii").split("-----") + idx = lines.index("BEGIN PRIVATE KEY") + if idx == -1: + raise ValueError("PEM has no PRIVATE KEY") + prv_raw = standard_b64decode(lines[idx + 1]) + pki = PrivateKeyInfo().decod(prv_raw) + prv = prv_unmarshal(bytes(OctetString().decod(bytes(pki["privateKey"])))) +else: + prv_raw = urandom(ai["prv_len"]) + out = stdout if args.out_key is None else open(args.out_key, "w") + print("-----BEGIN PRIVATE KEY-----", file=out) + print(pem(PrivateKeyInfo(( + ("version", Integer(0)), + ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier(( + ("algorithm", ai["key_algorithm"]), + ("parameters", Any(key_params)), + ))), + ("privateKey", PrivateKey(OctetString(prv_raw).encode())), + ))), file=out) + print("-----END PRIVATE KEY-----", file=out) + if args.only_key: + sys_exit() + prv = prv_unmarshal(prv_raw) + +curve = ai["curve"] +pub_raw = pub_marshal(public_key(curve, prv)) +rdn = [RelativeDistinguishedName(( + AttributeTypeAndValue(( + ("type", AttributeType(id_at_commonName)), + ("value", AttributeValue(PrintableString(args.cn))), + )), +))] +if args.country: + rdn.append(RelativeDistinguishedName(( + AttributeTypeAndValue(( + ("type", AttributeType(id_at_countryName)), + ("value", AttributeValue(PrintableString(args.country))), + )), + ))) +subj = Name(("rdnSequence", RDNSequence(rdn))) +not_before = datetime.utcnow() +not_after = not_before + timedelta(days=365 * (10 if args.ca else 1)) +ai_sign = AlgorithmIdentifier(( + ("algorithm", (ai if ca_ai is None else ca_ai)["sign_algorithm"]), +)) +exts = [ + Extension(( + ("extnID", id_ce_subjectKeyIdentifier), + ("extnValue", OctetString( + SubjectKeyIdentifier(GOST34112012256(pub_raw).digest()[:20]).encode() + )), + )), + Extension(( + ("extnID", id_ce_keyUsage), + ("critical", Boolean(True)), + ("extnValue", OctetString(KeyUsage( + ("keyCertSign" if args.ca else "digitalSignature",), + ).encode())), + )), +] +if args.ca: + exts.append(Extension(( + ("extnID", id_ce_basicConstraints), + ("critical", Boolean(True)), + ("extnValue", OctetString(BasicConstraints(( + ("cA", Boolean(True)), + )).encode())), + ))) +else: + exts.append(Extension(( + ("extnID", id_ce_subjectAltName), + ("extnValue", OctetString( + SubjectAltName(( + GeneralName(("dNSName", IA5String(args.cn))), + )).encode() + )), + ))) +if ca_ai is not None: + caKeyId = [ + bytes(SubjectKeyIdentifier().decod(bytes(ext["extnValue"]))) + for ext in ca_cert["tbsCertificate"]["extensions"] + if ext["extnID"] == id_ce_subjectKeyIdentifier + ][0] + exts.append(Extension(( + ("extnID", id_ce_authorityKeyIdentifier), + ("extnValue", OctetString(AuthorityKeyIdentifier(( + ("keyIdentifier", KeyIdentifier(caKeyId)), + )).encode())), + ))) + +serial = ( + bytes2long(GOST34112012256(urandom(16)).digest()[:20]) + if args.serial is None else int(args.serial) +) +tbs = TBSCertificate(( + ("version", Version("v3")), + ("serialNumber", CertificateSerialNumber(serial)), + ("signature", ai_sign), + ("issuer", subj if ca_ai is None else ca_subj), + ("validity", Validity(( + ("notBefore", Time(("utcTime", UTCTime(not_before)))), + ("notAfter", Time(("utcTime", UTCTime(not_after)))), + ))), + ("subject", subj), + ("subjectPublicKeyInfo", SubjectPublicKeyInfo(( + ("algorithm", AlgorithmIdentifier(( + ("algorithm", ai["key_algorithm"]), + ("parameters", Any(key_params)), + ))), + ("subjectPublicKey", BitString(OctetString(pub_raw).encode())), + ))), + ("extensions", Extensions(exts)), +)) +cert = Certificate(( + ("tbsCertificate", tbs), + ("signatureAlgorithm", ai_sign), + ("signatureValue", BitString( + sign(curve, prv, ai["hasher"](tbs.encode()).digest()[::-1]) + if ca_ai is None else + sign(ca_ai["curve"], ca_prv, ca_ai["hasher"](tbs.encode()).digest()[::-1]) + )), +)) +out = stdout if args.out_cert is None else open(args.out_cert, "w") +print("-----BEGIN CERTIFICATE-----", file=out) +print(pem(cert), file=out) +print("-----END CERTIFICATE-----", file=out) diff --git a/pygost-5.13/pygost/asn1schemas/cms.py b/pygost-5.13/pygost/asn1schemas/cms.py new file mode 100644 index 0000000..8028d2b --- /dev/null +++ b/pygost-5.13/pygost/asn1schemas/cms.py @@ -0,0 +1,431 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""CMS related structures (**NOT COMPLETE**) +""" + +from pyderasn import Any +from pyderasn import BitString +from pyderasn import Choice +from pyderasn import Integer +from pyderasn import ObjectIdentifier +from pyderasn import OctetString +from pyderasn import Sequence +from pyderasn import SequenceOf +from pyderasn import SetOf +from pyderasn import tag_ctxc +from pyderasn import tag_ctxp + +from pygost.asn1schemas.oids import id_cms_mac_attr +from pygost.asn1schemas.oids import id_contentType +from pygost.asn1schemas.oids import id_digestedData +from pygost.asn1schemas.oids import id_encryptedData +from pygost.asn1schemas.oids import id_envelopedData +from pygost.asn1schemas.oids import id_Gost28147_89 +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm_omac +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_wrap_kexp15 +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm_omac +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_wrap_kexp15 +from pygost.asn1schemas.oids import id_messageDigest +from pygost.asn1schemas.oids import id_signedData +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 +from pygost.asn1schemas.x509 import AlgorithmIdentifier +from pygost.asn1schemas.x509 import Certificate +from pygost.asn1schemas.x509 import CertificateSerialNumber +from pygost.asn1schemas.x509 import Name +from pygost.asn1schemas.x509 import SubjectPublicKeyInfo + + +class CMSVersion(Integer): + pass + + +class ContentType(ObjectIdentifier): + pass + + +class IssuerAndSerialNumber(Sequence): + schema = ( + ("issuer", Name()), + ("serialNumber", CertificateSerialNumber()), + ) + + +class KeyIdentifier(OctetString): + pass + + +class SubjectKeyIdentifier(KeyIdentifier): + pass + + +class RecipientIdentifier(Choice): + schema = ( + ("issuerAndSerialNumber", IssuerAndSerialNumber()), + ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))), + ) + + +class Gost2814789Key(OctetString): + bounds = (32, 32) + + +class Gost2814789MAC(OctetString): + bounds = (4, 4) + + +class Gost2814789EncryptedKey(Sequence): + schema = ( + ("encryptedKey", Gost2814789Key()), + ("maskKey", Gost2814789Key(impl=tag_ctxp(0), optional=True)), + ("macKey", Gost2814789MAC()), + ) + + +class GostR34102001TransportParameters(Sequence): + schema = ( + ("encryptionParamSet", ObjectIdentifier()), + ("ephemeralPublicKey", SubjectPublicKeyInfo( + impl=tag_ctxc(0), + optional=True, + )), + ("ukm", OctetString()), + ) + + +class GostR3410KeyTransport(Sequence): + schema = ( + ("sessionEncryptedKey", Gost2814789EncryptedKey()), + ("transportParameters", GostR34102001TransportParameters( + impl=tag_ctxc(0), + optional=True, + )), + ) + + +class GostR3410KeyTransport2019(Sequence): + schema = ( + ("encryptedKey", OctetString()), + ("ephemeralPublicKey", SubjectPublicKeyInfo()), + ("ukm", OctetString()), + ) + + +class GostR341012KEGParameters(Sequence): + schema = ( + ("algorithm", ObjectIdentifier()), + ) + + +class KeyEncryptionAlgorithmIdentifier(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), { + id_gostr3412_2015_magma_wrap_kexp15: GostR341012KEGParameters(), + id_gostr3412_2015_kuznyechik_wrap_kexp15: GostR341012KEGParameters(), + }), + (("..", "encryptedKey"), { + id_tc26_gost3410_2012_256: GostR3410KeyTransport(), + id_tc26_gost3410_2012_512: GostR3410KeyTransport(), + id_gostr3412_2015_magma_wrap_kexp15: GostR3410KeyTransport2019(), + id_gostr3412_2015_kuznyechik_wrap_kexp15: GostR3410KeyTransport2019(), + }), + (("..", "recipientEncryptedKeys", any, "encryptedKey"), { + id_tc26_gost3410_2012_256: Gost2814789EncryptedKey(), + id_tc26_gost3410_2012_512: Gost2814789EncryptedKey(), + }), + ))), + ("parameters", Any(optional=True)), + ) + + +class EncryptedKey(OctetString): + pass + + +class KeyTransRecipientInfo(Sequence): + schema = ( + ("version", CMSVersion()), + ("rid", RecipientIdentifier()), + ("keyEncryptionAlgorithm", KeyEncryptionAlgorithmIdentifier()), + ("encryptedKey", EncryptedKey()), + ) + + +class OriginatorPublicKey(Sequence): + schema = ( + ("algorithm", AlgorithmIdentifier()), + ("publicKey", BitString()), + ) + + +class OriginatorIdentifierOrKey(Choice): + schema = ( + ("issuerAndSerialNumber", IssuerAndSerialNumber()), + ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))), + ("originatorKey", OriginatorPublicKey(impl=tag_ctxc(1))), + ) + + +class UserKeyingMaterial(OctetString): + pass + + +class KeyAgreeRecipientIdentifier(Choice): + schema = ( + ("issuerAndSerialNumber", IssuerAndSerialNumber()), + # ("rKeyId", RecipientKeyIdentifier(impl=tag_ctxc(0))), + ) + + +class RecipientEncryptedKey(Sequence): + schema = ( + ("rid", KeyAgreeRecipientIdentifier()), + ("encryptedKey", EncryptedKey()), + ) + + +class RecipientEncryptedKeys(SequenceOf): + schema = RecipientEncryptedKey() + + +class KeyAgreeRecipientInfo(Sequence): + schema = ( + ("version", CMSVersion(3)), + ("originator", OriginatorIdentifierOrKey(expl=tag_ctxc(0))), + ("ukm", UserKeyingMaterial(expl=tag_ctxc(1), optional=True)), + ("keyEncryptionAlgorithm", KeyEncryptionAlgorithmIdentifier()), + ("recipientEncryptedKeys", RecipientEncryptedKeys()), + ) + + +class RecipientInfo(Choice): + schema = ( + ("ktri", KeyTransRecipientInfo()), + ("kari", KeyAgreeRecipientInfo(impl=tag_ctxc(1))), + # ("kekri", KEKRecipientInfo(impl=tag_ctxc(2))), + # ("pwri", PasswordRecipientInfo(impl=tag_ctxc(3))), + # ("ori", OtherRecipientInfo(impl=tag_ctxc(4))), + ) + + +class RecipientInfos(SetOf): + schema = RecipientInfo() + bounds = (1, float("+inf")) + + +class Gost2814789IV(OctetString): + bounds = (8, 8) + + +class Gost2814789Parameters(Sequence): + schema = ( + ("iv", Gost2814789IV()), + ("encryptionParamSet", ObjectIdentifier()), + ) + + +class Gost341215EncryptionParameters(Sequence): + schema = ( + ("ukm", OctetString()), + ) + + +class ContentEncryptionAlgorithmIdentifier(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), { + id_Gost28147_89: Gost2814789Parameters(), + id_gostr3412_2015_magma_ctracpkm: Gost341215EncryptionParameters(), + id_gostr3412_2015_kuznyechik_ctracpkm: Gost341215EncryptionParameters(), + id_gostr3412_2015_magma_ctracpkm_omac: Gost341215EncryptionParameters(), + id_gostr3412_2015_kuznyechik_ctracpkm_omac: Gost341215EncryptionParameters(), + }), + ))), + ("parameters", Any(optional=True)), + ) + + +class EncryptedContent(OctetString): + pass + + +class EncryptedContentInfo(Sequence): + schema = ( + ("contentType", ContentType()), + ("contentEncryptionAlgorithm", ContentEncryptionAlgorithmIdentifier()), + ("encryptedContent", EncryptedContent(impl=tag_ctxp(0), optional=True)), + ) + + +class Digest(OctetString): + pass + + +class AttributeValue(Any): + pass + + +class AttributeValues(SetOf): + schema = AttributeValue() + + +class EncryptedMac(OctetString): + pass + + +class Attribute(Sequence): + schema = ( + ("attrType", ObjectIdentifier(defines=( + (("attrValues",), { + id_contentType: ObjectIdentifier(), + id_messageDigest: Digest(), + id_cms_mac_attr: EncryptedMac(), + },), + ))), + ("attrValues", AttributeValues()), + ) + + +class UnprotectedAttributes(SetOf): + schema = Attribute() + bounds = (1, float("+inf")) + + +class CertificateChoices(Choice): + schema = ( + ("certificate", Certificate()), + # ("extendedCertificate", OctetString(impl=tag_ctxp(0))), + # ("v1AttrCert", AttributeCertificateV1(impl=tag_ctxc(1))), # V1 is osbolete + # ("v2AttrCert", AttributeCertificateV2(impl=tag_ctxc(2))), + # ("other", OtherCertificateFormat(impl=tag_ctxc(3))), + ) + + +class CertificateSet(SetOf): + schema = CertificateChoices() + + +class OriginatorInfo(Sequence): + schema = ( + ("certs", CertificateSet(impl=tag_ctxc(0), optional=True)), + # ("crls", RevocationInfoChoices(impl=tag_ctxc(1), optional=True)), + ) + + +class EnvelopedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("originatorInfo", OriginatorInfo(impl=tag_ctxc(0), optional=True)), + ("recipientInfos", RecipientInfos()), + ("encryptedContentInfo", EncryptedContentInfo()), + ("unprotectedAttrs", UnprotectedAttributes(impl=tag_ctxc(1), optional=True)), + ) + + +class EncapsulatedContentInfo(Sequence): + schema = ( + ("eContentType", ContentType()), + ("eContent", OctetString(expl=tag_ctxc(0), optional=True)), + ) + + +class SignerIdentifier(Choice): + schema = ( + ("issuerAndSerialNumber", IssuerAndSerialNumber()), + ("subjectKeyIdentifier", SubjectKeyIdentifier(impl=tag_ctxp(0))), + ) + + +class DigestAlgorithmIdentifiers(SetOf): + schema = AlgorithmIdentifier() + + +class DigestAlgorithmIdentifier(AlgorithmIdentifier): + pass + + +class SignatureAlgorithmIdentifier(AlgorithmIdentifier): + pass + + +class SignatureValue(OctetString): + pass + + +class SignedAttributes(SetOf): + schema = Attribute() + bounds = (1, float("+inf")) + + +class SignerInfo(Sequence): + schema = ( + ("version", CMSVersion()), + ("sid", SignerIdentifier()), + ("digestAlgorithm", DigestAlgorithmIdentifier()), + ("signedAttrs", SignedAttributes(impl=tag_ctxc(0), optional=True)), + ("signatureAlgorithm", SignatureAlgorithmIdentifier()), + ("signature", SignatureValue()), + # ("unsignedAttrs", UnsignedAttributes(impl=tag_ctxc(1), optional=True)), + ) + + +class SignerInfos(SetOf): + schema = SignerInfo() + + +class SignedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("digestAlgorithms", DigestAlgorithmIdentifiers()), + ("encapContentInfo", EncapsulatedContentInfo()), + ("certificates", CertificateSet(impl=tag_ctxc(0), optional=True)), + # ("crls", RevocationInfoChoices(impl=tag_ctxc(1), optional=True)), + ("signerInfos", SignerInfos()), + ) + + +class DigestedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("digestAlgorithm", DigestAlgorithmIdentifier()), + ("encapContentInfo", EncapsulatedContentInfo()), + ("digest", Digest()), + ) + + +class EncryptedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("encryptedContentInfo", EncryptedContentInfo()), + ("unprotectedAttrs", UnprotectedAttributes(impl=tag_ctxc(1), optional=True)), + ) + + +class ContentInfo(Sequence): + schema = ( + ("contentType", ContentType(defines=( + (("content",), { + id_digestedData: DigestedData(), + id_encryptedData: EncryptedData(), + id_envelopedData: EnvelopedData(), + id_signedData: SignedData(), + }), + ))), + ("content", Any(expl=tag_ctxc(0))), + ) diff --git a/pygost-5.13/pygost/asn1schemas/oids.py b/pygost-5.13/pygost/asn1schemas/oids.py new file mode 100644 index 0000000..4638900 --- /dev/null +++ b/pygost-5.13/pygost/asn1schemas/oids.py @@ -0,0 +1,60 @@ +from pyderasn import ObjectIdentifier + + +id_at_commonName = ObjectIdentifier("2.5.4.3") +id_at_countryName = ObjectIdentifier("2.5.4.6") +id_at_localityName = ObjectIdentifier("2.5.4.7") +id_at_stateOrProvinceName = ObjectIdentifier("2.5.4.8") +id_at_organizationName = ObjectIdentifier("2.5.4.10") + +id_pkcs7 = ObjectIdentifier("1.2.840.113549.1.7") +id_data = id_pkcs7 + (1,) +id_signedData = id_pkcs7 + (2,) +id_envelopedData = id_pkcs7 + (3,) +id_digestedData = id_pkcs7 + (5,) +id_encryptedData = id_pkcs7 + (6,) + +id_pkcs9 = ObjectIdentifier("1.2.840.113549.1.9") +id_contentType = id_pkcs9 + (3,) +id_messageDigest = id_pkcs9 + (4,) +id_pkcs9_certTypes_x509Certificate = ObjectIdentifier("1.2.840.113549.1.9.22.1") +id_pkcs12_bagtypes_keyBag = ObjectIdentifier("1.2.840.113549.1.12.10.1.1") +id_pkcs12_bagtypes_pkcs8ShroudedKeyBag = ObjectIdentifier("1.2.840.113549.1.12.10.1.2") +id_pkcs12_bagtypes_certBag = ObjectIdentifier("1.2.840.113549.1.12.10.1.3") + +id_Gost28147_89 = ObjectIdentifier("1.2.643.2.2.21") +id_GostR3410_2001_TestParamSet = ObjectIdentifier("1.2.643.2.2.35.0") +id_cms_mac_attr = ObjectIdentifier("1.2.643.7.1.0.6.1.1") +id_tc26_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.1.1") +id_tc26_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.1.2") +id_tc26_gost3411_2012_256 = ObjectIdentifier("1.2.643.7.1.1.2.2") +id_tc26_gost3411_2012_512 = ObjectIdentifier("1.2.643.7.1.1.2.3") +id_tc26_signwithdigest_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.3.2") +id_tc26_signwithdigest_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.3.3") +id_gostr3412_2015_magma_ctracpkm = ObjectIdentifier("1.2.643.7.1.1.5.1.1") +id_gostr3412_2015_magma_ctracpkm_omac = ObjectIdentifier("1.2.643.7.1.1.5.1.2") +id_gostr3412_2015_kuznyechik_ctracpkm = ObjectIdentifier("1.2.643.7.1.1.5.2.1") +id_gostr3412_2015_kuznyechik_ctracpkm_omac = ObjectIdentifier("1.2.643.7.1.1.5.2.2") +id_tc26_agreement_gost3410_2012_256 = ObjectIdentifier("1.2.643.7.1.1.6.1") +id_tc26_agreement_gost3410_2012_512 = ObjectIdentifier("1.2.643.7.1.1.6.2") +id_gostr3412_2015_magma_wrap_kexp15 = ObjectIdentifier("1.2.643.7.1.1.7.1.1") +id_gostr3412_2015_kuznyechik_wrap_kexp15 = ObjectIdentifier("1.2.643.7.1.1.7.2.1") +id_tc26_gost3410_2012_256_paramSetA = ObjectIdentifier("1.2.643.7.1.2.1.1.1") +id_tc26_gost3410_2012_256_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.1.2") +id_tc26_gost3410_2012_256_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.1.3") +id_tc26_gost3410_2012_256_paramSetD = ObjectIdentifier("1.2.643.7.1.2.1.1.4") +id_tc26_gost3410_2012_512_paramSetTest = ObjectIdentifier("1.2.643.7.1.2.1.2.0") +id_tc26_gost3410_2012_512_paramSetA = ObjectIdentifier("1.2.643.7.1.2.1.2.1") +id_tc26_gost3410_2012_512_paramSetB = ObjectIdentifier("1.2.643.7.1.2.1.2.2") +id_tc26_gost3410_2012_512_paramSetC = ObjectIdentifier("1.2.643.7.1.2.1.2.3") +id_tc26_gost_28147_param_Z = ObjectIdentifier("1.2.643.7.1.2.5.1.1") + +id_pbes2 = ObjectIdentifier("1.2.840.113549.1.5.13") +id_pbkdf2 = ObjectIdentifier("1.2.840.113549.1.5.12") + +id_at_commonName = ObjectIdentifier("2.5.4.3") +id_ce_basicConstraints = ObjectIdentifier("2.5.29.19") +id_ce_subjectKeyIdentifier = ObjectIdentifier("2.5.29.14") +id_ce_keyUsage = ObjectIdentifier("2.5.29.15") +id_ce_subjectAltName = ObjectIdentifier("2.5.29.17") +id_ce_authorityKeyIdentifier = ObjectIdentifier("2.5.29.35") diff --git a/pygost-5.13/pygost/asn1schemas/pfx.py b/pygost-5.13/pygost/asn1schemas/pfx.py new file mode 100644 index 0000000..27a87d0 --- /dev/null +++ b/pygost-5.13/pygost/asn1schemas/pfx.py @@ -0,0 +1,250 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""PKCS #12 related structures (**NOT COMPLETE**) +""" + +from pyderasn import Any +from pyderasn import Choice +from pyderasn import Integer +from pyderasn import ObjectIdentifier +from pyderasn import OctetString +from pyderasn import Sequence +from pyderasn import SequenceOf +from pyderasn import SetOf +from pyderasn import tag_ctxc +from pyderasn import tag_ctxp + +from pygost.asn1schemas.cms import CMSVersion +from pygost.asn1schemas.cms import ContentType +from pygost.asn1schemas.cms import Gost2814789Parameters +from pygost.asn1schemas.cms import Gost341215EncryptionParameters +from pygost.asn1schemas.oids import id_data +from pygost.asn1schemas.oids import id_encryptedData +from pygost.asn1schemas.oids import id_Gost28147_89 +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm +from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm_omac +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm +from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm_omac +from pygost.asn1schemas.oids import id_pbes2 +from pygost.asn1schemas.oids import id_pbkdf2 +from pygost.asn1schemas.oids import id_pkcs9_certTypes_x509Certificate +from pygost.asn1schemas.prvkey import PrivateKeyInfo +from pygost.asn1schemas.x509 import AlgorithmIdentifier +from pygost.asn1schemas.x509 import Certificate + + +class PBKDF2Salt(Choice): + schema = ( + ("specified", OctetString()), + # ("otherSource", PBKDF2SaltSources()), + ) + + +id_hmacWithSHA1 = ObjectIdentifier("1.2.840.113549.2.7") + + +class PBKDF2PRFs(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(default=id_hmacWithSHA1)), + ("parameters", Any(optional=True)), + ) + + +class IterationCount(Integer): + bounds = (1, float("+inf")) + + +class KeyLength(Integer): + bounds = (1, float("+inf")) + + +class PBKDF2Params(Sequence): + schema = ( + ("salt", PBKDF2Salt()), + ("iterationCount", IterationCount(optional=True)), + ("keyLength", KeyLength(optional=True)), + ("prf", PBKDF2PRFs()), + ) + + +class PBES2KDFs(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), {id_pbkdf2: PBKDF2Params()}), + ))), + ("parameters", Any(optional=True)), + ) + + +class PBES2Encs(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), { + id_Gost28147_89: Gost2814789Parameters(), + id_gostr3412_2015_magma_ctracpkm: Gost341215EncryptionParameters(), + id_gostr3412_2015_magma_ctracpkm_omac: Gost341215EncryptionParameters(), + id_gostr3412_2015_kuznyechik_ctracpkm: Gost341215EncryptionParameters(), + id_gostr3412_2015_kuznyechik_ctracpkm_omac: Gost341215EncryptionParameters(), + }), + ))), + ("parameters", Any(optional=True)), + ) + + +class PBES2Params(Sequence): + schema = ( + ("keyDerivationFunc", PBES2KDFs()), + ("encryptionScheme", PBES2Encs()), + ) + + +class EncryptionAlgorithmIdentifier(AlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), {id_pbes2: PBES2Params()}), + ))), + ("parameters", Any(optional=True)), + ) + + +class ContentEncryptionAlgorithmIdentifier(EncryptionAlgorithmIdentifier): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), {id_pbes2: PBES2Params()}), + ))), + ("parameters", Any(optional=True)), + ) + + +class EncryptedContent(OctetString): + pass + + +class EncryptedContentInfo(Sequence): + schema = ( + ("contentType", ContentType()), + ("contentEncryptionAlgorithm", ContentEncryptionAlgorithmIdentifier()), + ("encryptedContent", EncryptedContent(impl=tag_ctxp(0), optional=True)), + ) + + +class EncryptedData(Sequence): + schema = ( + ("version", CMSVersion()), + ("encryptedContentInfo", EncryptedContentInfo()), + # ("unprotectedAttrs", UnprotectedAttributes(impl=tag_ctxc(1), optional=True)), + ) + + +class PKCS12BagSet(Any): + pass + + +class AttrValue(SetOf): + schema = Any() + + +class PKCS12Attribute(Sequence): + schema = ( + ("attrId", ObjectIdentifier()), + ("attrValue", AttrValue()), + ) + + +class PKCS12Attributes(SetOf): + schema = PKCS12Attribute() + + +class SafeBag(Sequence): + schema = ( + ("bagId", ObjectIdentifier(defines=( + (("bagValue",), {id_encryptedData: EncryptedData()}), + ))), + ("bagValue", PKCS12BagSet(expl=tag_ctxc(0))), + ("bagAttributes", PKCS12Attributes(optional=True)), + ) + + +class SafeContents(SequenceOf): + schema = SafeBag() + + +OctetStringSafeContents = SafeContents(expl=OctetString.tag_default) + + +class AuthSafe(Sequence): + schema = ( + ("contentType", ContentType(defines=( + (("content",), {id_data: OctetStringSafeContents()}), + ))), + ("content", Any(expl=tag_ctxc(0))), + ) + + +class DigestInfo(Sequence): + schema = ( + ("digestAlgorithm", AlgorithmIdentifier()), + ("digest", OctetString()), + ) + + +class MacData(Sequence): + schema = ( + ("mac", DigestInfo()), + ("macSalt", OctetString()), + ("iterations", Integer(default=1)), + ) + + +class PFX(Sequence): + schema = ( + ("version", Integer(default=1)), + ("authSafe", AuthSafe()), + ("macData", MacData(optional=True)), + ) + + +class EncryptedPrivateKeyInfo(Sequence): + schema = ( + ("encryptionAlgorithm", EncryptionAlgorithmIdentifier()), + ("encryptedData", OctetString()), + ) + + +class PKCS8ShroudedKeyBag(EncryptedPrivateKeyInfo): + pass + + +OctetStringX509Certificate = Certificate(expl=OctetString.tag_default) + + +class CertTypes(Any): + pass + + +class CertBag(Sequence): + schema = ( + ("certId", ObjectIdentifier(defines=( + (("certValue",), { + id_pkcs9_certTypes_x509Certificate: OctetStringX509Certificate(), + }), + ))), + ("certValue", CertTypes(expl=tag_ctxc(0))), + ) + + +class KeyBag(PrivateKeyInfo): + pass diff --git a/pygost-5.13/pygost/asn1schemas/pkcs10.py b/pygost-5.13/pygost/asn1schemas/pkcs10.py new file mode 100644 index 0000000..dce45dd --- /dev/null +++ b/pygost-5.13/pygost/asn1schemas/pkcs10.py @@ -0,0 +1,49 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""PKCS #10 related structures (**NOT COMPLETE**) +""" + +from pyderasn import BitString +from pyderasn import Integer +from pyderasn import Sequence +from pyderasn import SetOf +from pyderasn import tag_ctxc + +from pygost.asn1schemas.cms import Attribute +from pygost.asn1schemas.x509 import AlgorithmIdentifier +from pygost.asn1schemas.x509 import Name +from pygost.asn1schemas.x509 import SubjectPublicKeyInfo + + +class Attributes(SetOf): + schema = Attribute() + + +class CertificationRequestInfo(Sequence): + schema = ( + ("version", Integer(0)), + ("subject", Name()), + ("subjectPKInfo", SubjectPublicKeyInfo()), + ("attributes", Attributes(impl=tag_ctxc(0))), + ) + + +class CertificationRequest(Sequence): + schema = ( + ("certificationRequestInfo", CertificationRequestInfo()), + ("signatureAlgorithm", AlgorithmIdentifier()), + ("signature", BitString()), + ) diff --git a/pygost-5.13/pygost/asn1schemas/prvkey.py b/pygost-5.13/pygost/asn1schemas/prvkey.py new file mode 100644 index 0000000..7da2533 --- /dev/null +++ b/pygost-5.13/pygost/asn1schemas/prvkey.py @@ -0,0 +1,100 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from pyderasn import Any +from pyderasn import BitString +from pyderasn import Choice +from pyderasn import Integer +from pyderasn import Null +from pyderasn import ObjectIdentifier +from pyderasn import OctetString +from pyderasn import Sequence +from pyderasn import SetOf +from pyderasn import tag_ctxc +from pyderasn import tag_ctxp + +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 +from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 +from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters + + +class ECParameters(Choice): + schema = ( + ("namedCurve", ObjectIdentifier()), + ("implicitCurve", Null()), + # ("specifiedCurve", SpecifiedECDomain()), + ) + + +ecPrivkeyVer1 = Integer(1) + + +class ECPrivateKey(Sequence): + schema = ( + ("version", Integer(ecPrivkeyVer1)), + ("privateKey", OctetString()), + ("parameters", ECParameters(expl=tag_ctxc(0), optional=True)), + ("publicKey", BitString(expl=tag_ctxc(1), optional=True)), + ) + + +class PrivateKeyAlgorithmIdentifier(Sequence): + schema = ( + ("algorithm", ObjectIdentifier(defines=( + (("parameters",), { + id_tc26_gost3410_2012_256: GostR34102012PublicKeyParameters(), + id_tc26_gost3410_2012_512: GostR34102012PublicKeyParameters(), + }), + ))), + ("parameters", Any(optional=True)), + ) + + +class PrivateKey(OctetString): + pass + + +class AttributeValue(Any): + pass + + +class AttributeValues(SetOf): + schema = AttributeValue() + + +class Attribute(Sequence): + schema = ( + ("attrType", ObjectIdentifier()), + ("attrValues", AttributeValues()), + ) + + +class Attributes(SetOf): + schema = Attribute() + + +class PublicKey(BitString): + pass + + +class PrivateKeyInfo(Sequence): + schema = ( + ("version", Integer(0)), + ("privateKeyAlgorithm", PrivateKeyAlgorithmIdentifier()), + ("privateKey", PrivateKey()), + ("attributes", Attributes(impl=tag_ctxc(0), optional=True)), + ("publicKey", PublicKey(impl=tag_ctxp(1), optional=True)), + ) diff --git a/pygost-5.13/pygost/asn1schemas/x509.py b/pygost-5.13/pygost/asn1schemas/x509.py new file mode 100644 index 0000000..86ad7da --- /dev/null +++ b/pygost-5.13/pygost/asn1schemas/x509.py @@ -0,0 +1,262 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +""":rfc:`5280` related structures (**NOT COMPLETE**) + +They are taken from `PyDERASN +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST 28147-89 block cipher + +This is implementation of :rfc:`5830` ECB, CNT, CFB and :rfc:`4357` +CBC modes of operation. N1, N2, K names are taken according to +specification's terminology. CNT and CFB modes can work with arbitrary +data lengths. +""" + +from functools import partial + +from pygost.gost3413 import pad2 +from pygost.gost3413 import pad_size +from pygost.gost3413 import unpad2 +from pygost.utils import hexdec +from pygost.utils import strxor +from pygost.utils import xrange + + +KEYSIZE = 32 +BLOCKSIZE = 8 +C1 = 0x01010104 +C2 = 0x01010101 + +# Sequence of K_i S-box applying for encryption and decryption +SEQ_ENCRYPT = ( + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 7, 6, 5, 4, 3, 2, 1, 0, +) +SEQ_DECRYPT = ( + 0, 1, 2, 3, 4, 5, 6, 7, + 7, 6, 5, 4, 3, 2, 1, 0, + 7, 6, 5, 4, 3, 2, 1, 0, + 7, 6, 5, 4, 3, 2, 1, 0, +) + +# S-box parameters +DEFAULT_SBOX = "id-Gost28147-89-CryptoPro-A-ParamSet" +SBOXES = { + "id-Gost28147-89-TestParamSet": ( + (4, 2, 15, 5, 9, 1, 0, 8, 14, 3, 11, 12, 13, 7, 10, 6), + (12, 9, 15, 14, 8, 1, 3, 10, 2, 7, 4, 13, 6, 0, 11, 5), + (13, 8, 14, 12, 7, 3, 9, 10, 1, 5, 2, 4, 6, 15, 0, 11), + (14, 9, 11, 2, 5, 15, 7, 1, 0, 13, 12, 6, 10, 4, 3, 8), + (3, 14, 5, 9, 6, 8, 0, 13, 10, 11, 7, 12, 2, 1, 15, 4), + (8, 15, 6, 11, 1, 9, 12, 5, 13, 3, 7, 10, 0, 14, 2, 4), + (9, 11, 12, 0, 3, 6, 7, 5, 4, 8, 14, 15, 1, 10, 2, 13), + (12, 6, 5, 2, 11, 0, 9, 13, 3, 14, 7, 10, 15, 4, 1, 8), + ), + "id-Gost28147-89-CryptoPro-A-ParamSet": ( + (9, 6, 3, 2, 8, 11, 1, 7, 10, 4, 14, 15, 12, 0, 13, 5), + (3, 7, 14, 9, 8, 10, 15, 0, 5, 2, 6, 12, 11, 4, 13, 1), + (14, 4, 6, 2, 11, 3, 13, 8, 12, 15, 5, 10, 0, 7, 1, 9), + (14, 7, 10, 12, 13, 1, 3, 9, 0, 2, 11, 4, 15, 8, 5, 6), + (11, 5, 1, 9, 8, 13, 15, 0, 14, 4, 2, 3, 12, 7, 10, 6), + (3, 10, 13, 12, 1, 2, 0, 11, 7, 5, 9, 4, 8, 15, 14, 6), + (1, 13, 2, 9, 7, 10, 6, 0, 8, 12, 4, 5, 15, 3, 11, 14), + (11, 10, 15, 5, 0, 12, 14, 8, 6, 2, 3, 9, 1, 7, 13, 4), + ), + "id-Gost28147-89-CryptoPro-B-ParamSet": ( + (8, 4, 11, 1, 3, 5, 0, 9, 2, 14, 10, 12, 13, 6, 7, 15), + (0, 1, 2, 10, 4, 13, 5, 12, 9, 7, 3, 15, 11, 8, 6, 14), + (14, 12, 0, 10, 9, 2, 13, 11, 7, 5, 8, 15, 3, 6, 1, 4), + (7, 5, 0, 13, 11, 6, 1, 2, 3, 10, 12, 15, 4, 14, 9, 8), + (2, 7, 12, 15, 9, 5, 10, 11, 1, 4, 0, 13, 6, 8, 14, 3), + (8, 3, 2, 6, 4, 13, 14, 11, 12, 1, 7, 15, 10, 0, 9, 5), + (5, 2, 10, 11, 9, 1, 12, 3, 7, 4, 13, 0, 6, 15, 8, 14), + (0, 4, 11, 14, 8, 3, 7, 1, 10, 2, 9, 6, 15, 13, 5, 12), + ), + "id-Gost28147-89-CryptoPro-C-ParamSet": ( + (1, 11, 12, 2, 9, 13, 0, 15, 4, 5, 8, 14, 10, 7, 6, 3), + (0, 1, 7, 13, 11, 4, 5, 2, 8, 14, 15, 12, 9, 10, 6, 3), + (8, 2, 5, 0, 4, 9, 15, 10, 3, 7, 12, 13, 6, 14, 1, 11), + (3, 6, 0, 1, 5, 13, 10, 8, 11, 2, 9, 7, 14, 15, 12, 4), + (8, 13, 11, 0, 4, 5, 1, 2, 9, 3, 12, 14, 6, 15, 10, 7), + (12, 9, 11, 1, 8, 14, 2, 4, 7, 3, 6, 5, 10, 0, 15, 13), + (10, 9, 6, 8, 13, 14, 2, 0, 15, 3, 5, 11, 4, 1, 12, 7), + (7, 4, 0, 5, 10, 2, 15, 14, 12, 6, 1, 11, 13, 9, 3, 8), + ), + "id-Gost28147-89-CryptoPro-D-ParamSet": ( + (15, 12, 2, 10, 6, 4, 5, 0, 7, 9, 14, 13, 1, 11, 8, 3), + (11, 6, 3, 4, 12, 15, 14, 2, 7, 13, 8, 0, 5, 10, 9, 1), + (1, 12, 11, 0, 15, 14, 6, 5, 10, 13, 4, 8, 9, 3, 7, 2), + (1, 5, 14, 12, 10, 7, 0, 13, 6, 2, 11, 4, 9, 3, 15, 8), + (0, 12, 8, 9, 13, 2, 10, 11, 7, 3, 6, 5, 4, 14, 15, 1), + (8, 0, 15, 3, 2, 5, 14, 11, 1, 10, 4, 7, 12, 9, 13, 6), + (3, 0, 6, 15, 1, 14, 9, 2, 13, 8, 12, 4, 11, 10, 5, 7), + (1, 10, 6, 8, 15, 11, 0, 4, 12, 3, 5, 9, 7, 13, 2, 14), + ), + "id-tc26-gost-28147-param-Z": ( + (12, 4, 6, 2, 10, 5, 11, 9, 14, 8, 13, 7, 0, 3, 15, 1), + (6, 8, 2, 3, 9, 10, 5, 12, 1, 14, 4, 7, 11, 13, 0, 15), + (11, 3, 5, 8, 2, 15, 10, 13, 14, 1, 7, 4, 12, 9, 6, 0), + (12, 8, 2, 1, 13, 4, 15, 6, 7, 0, 10, 5, 3, 14, 9, 11), + (7, 15, 5, 10, 8, 1, 6, 13, 0, 9, 3, 14, 11, 4, 2, 12), + (5, 13, 15, 6, 9, 2, 12, 10, 11, 7, 8, 1, 4, 3, 14, 0), + (8, 14, 2, 5, 6, 9, 1, 12, 15, 4, 11, 0, 13, 10, 3, 7), + (1, 7, 14, 13, 0, 5, 8, 3, 4, 15, 10, 6, 9, 12, 11, 2), + ), + "id-GostR3411-94-TestParamSet": ( + (4, 10, 9, 2, 13, 8, 0, 14, 6, 11, 1, 12, 7, 15, 5, 3), + (14, 11, 4, 12, 6, 13, 15, 10, 2, 3, 8, 1, 0, 7, 5, 9), + (5, 8, 1, 13, 10, 3, 4, 2, 14, 15, 12, 7, 6, 0, 9, 11), + (7, 13, 10, 1, 0, 8, 9, 15, 14, 4, 6, 12, 11, 2, 5, 3), + (6, 12, 7, 1, 5, 15, 13, 8, 4, 10, 9, 14, 0, 3, 11, 2), + (4, 11, 10, 0, 7, 2, 1, 13, 3, 6, 8, 5, 9, 12, 15, 14), + (13, 11, 4, 1, 3, 15, 5, 9, 0, 10, 14, 7, 6, 8, 2, 12), + (1, 15, 13, 0, 5, 7, 10, 4, 9, 2, 3, 14, 6, 11, 8, 12), + ), + "id-GostR3411-94-CryptoProParamSet": ( + (10, 4, 5, 6, 8, 1, 3, 7, 13, 12, 14, 0, 9, 2, 11, 15), + (5, 15, 4, 0, 2, 13, 11, 9, 1, 7, 6, 3, 12, 14, 10, 8), + (7, 15, 12, 14, 9, 4, 1, 0, 3, 11, 5, 2, 6, 10, 8, 13), + (4, 10, 7, 12, 0, 15, 2, 8, 14, 1, 6, 5, 13, 11, 9, 3), + (7, 6, 4, 11, 9, 12, 2, 10, 1, 8, 0, 14, 15, 13, 3, 5), + (7, 6, 2, 4, 13, 9, 15, 0, 10, 1, 5, 11, 8, 14, 12, 3), + (13, 14, 4, 1, 7, 0, 5, 10, 3, 12, 8, 15, 6, 2, 9, 11), + (1, 3, 10, 9, 5, 11, 4, 15, 8, 6, 7, 14, 13, 0, 2, 12), + ), + "EACParamSet": ( + (11, 4, 8, 10, 9, 7, 0, 3, 1, 6, 2, 15, 14, 5, 12, 13), + (1, 7, 14, 9, 11, 3, 15, 12, 0, 5, 4, 6, 13, 10, 8, 2), + (7, 3, 1, 9, 2, 4, 13, 15, 8, 10, 12, 6, 5, 0, 11, 14), + (10, 5, 15, 7, 14, 11, 3, 9, 2, 8, 1, 12, 0, 4, 6, 13), + (0, 14, 6, 11, 9, 3, 8, 4, 12, 15, 10, 5, 13, 7, 1, 2), + (9, 2, 11, 12, 0, 4, 5, 6, 3, 15, 13, 8, 1, 7, 14, 10), + (4, 0, 14, 1, 5, 11, 8, 3, 12, 2, 9, 7, 6, 10, 13, 15), + (7, 14, 12, 13, 9, 4, 8, 15, 10, 2, 6, 0, 3, 11, 5, 1), + ), +} +SBOXES["AppliedCryptography"] = SBOXES["id-GostR3411-94-TestParamSet"] + + +def _K(s, _in): + """S-box substitution + + :param s: S-box + :param _in: 32-bit word + :returns: substituted 32-bit word + """ + return ( + (s[0][(_in >> 0) & 0x0F] << 0) + + (s[1][(_in >> 4) & 0x0F] << 4) + + (s[2][(_in >> 8) & 0x0F] << 8) + + (s[3][(_in >> 12) & 0x0F] << 12) + + (s[4][(_in >> 16) & 0x0F] << 16) + + (s[5][(_in >> 20) & 0x0F] << 20) + + (s[6][(_in >> 24) & 0x0F] << 24) + + (s[7][(_in >> 28) & 0x0F] << 28) + ) + + +def block2ns(data): + """Convert block to N1 and N2 integers + """ + data = bytearray(data) + return ( + data[0] | data[1] << 8 | data[2] << 16 | data[3] << 24, + data[4] | data[5] << 8 | data[6] << 16 | data[7] << 24, + ) + + +def ns2block(ns): + """Convert N1 and N2 integers to 8-byte block + """ + n1, n2 = ns + return bytes(bytearray(( + (n2 >> 0) & 0xFF, (n2 >> 8) & 0xFF, (n2 >> 16) & 0xFF, (n2 >> 24) & 0xFF, + (n1 >> 0) & 0xFF, (n1 >> 8) & 0xFF, (n1 >> 16) & 0xFF, (n1 >> 24) & 0xFF, + ))) + + +def _shift11(x): + """11-bit cyclic shift + """ + return ((x << 11) & (2 ** 32 - 1)) | ((x >> (32 - 11)) & (2 ** 32 - 1)) + + +def validate_key(key): + if len(key) != KEYSIZE: + raise ValueError("Invalid key size") + + +def validate_iv(iv): + if len(iv) != BLOCKSIZE: + raise ValueError("Invalid IV size") + + +def validate_sbox(sbox): + if sbox not in SBOXES: + raise ValueError("Unknown sbox supplied") + + +def xcrypt(seq, sbox, key, ns): + """Perform full-round single-block operation + + :param seq: sequence of K_i S-box applying (either encrypt or decrypt) + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bytes key: 256-bit encryption key + :param ns: N1 and N2 integers + :type ns: (int, int) + :returns: resulting N1 and N2 + :rtype: (int, int) + """ + s = SBOXES[sbox] + w = bytearray(key) + x = [ + w[0 + i * 4] | + w[1 + i * 4] << 8 | + w[2 + i * 4] << 16 | + w[3 + i * 4] << 24 for i in range(8) + ] + n1, n2 = ns + for i in seq: + n1, n2 = _shift11(_K(s, (n1 + x[i]) % (2 ** 32))) ^ n2, n1 + return n1, n2 + + +def encrypt(sbox, key, ns): + """Encrypt single block + """ + return xcrypt(SEQ_ENCRYPT, sbox, key, ns) + + +def decrypt(sbox, key, ns): + """Decrypt single block + """ + return xcrypt(SEQ_DECRYPT, sbox, key, ns) + + +def ecb(key, data, action, sbox=DEFAULT_SBOX): + """ECB mode of operation + + :param bytes key: encryption key + :param data: plaintext + :type data: bytes, multiple of BLOCKSIZE + :param func action: "encrypt"/"decrypt" + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :returns: ciphertext + :rtype: bytes + """ + validate_key(key) + validate_sbox(sbox) + if not data or len(data) % BLOCKSIZE != 0: + raise ValueError("Data is not blocksize aligned") + result = [] + for i in xrange(0, len(data), BLOCKSIZE): + result.append(ns2block(action( + sbox, key, block2ns(data[i:i + BLOCKSIZE]) + ))) + return b"".join(result) + + +ecb_encrypt = partial(ecb, action=encrypt) +ecb_decrypt = partial(ecb, action=decrypt) + + +def cbc_encrypt(key, data, iv=8 * b"\x00", pad=True, sbox=DEFAULT_SBOX, mesh=False): + """CBC encryption mode of operation + + :param bytes key: encryption key + :param bytes data: plaintext + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :type bool pad: perform ISO/IEC 7816-4 padding + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing + :returns: ciphertext + :rtype: bytes + + 34.13-2015 padding method 2 is used. + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + if not data: + raise ValueError("No data supplied") + if pad: + data = pad2(data, BLOCKSIZE) + if len(data) % BLOCKSIZE != 0: + raise ValueError("Data is not blocksize aligned") + ciphertext = [iv] + for i in xrange(0, len(data), BLOCKSIZE): + if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0: + key, _ = meshing(key, iv, sbox=sbox) + ciphertext.append(ns2block(encrypt(sbox, key, block2ns( + strxor(ciphertext[-1], data[i:i + BLOCKSIZE]) + )))) + return b"".join(ciphertext) + + +def cbc_decrypt(key, data, pad=True, sbox=DEFAULT_SBOX, mesh=False): + """CBC decryption mode of operation + + :param bytes key: encryption key + :param bytes data: ciphertext + :type bool pad: perform ISO/IEC 7816-4 unpadding after decryption + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing + :returns: plaintext + :rtype: bytes + """ + validate_key(key) + validate_sbox(sbox) + if not data or len(data) % BLOCKSIZE != 0: + raise ValueError("Data is not blocksize aligned") + if len(data) < 2 * BLOCKSIZE: + raise ValueError("There is no either data, or IV in ciphertext") + iv = data[:BLOCKSIZE] + plaintext = [] + for i in xrange(BLOCKSIZE, len(data), BLOCKSIZE): + if ( + mesh and + (i - BLOCKSIZE) >= MESH_MAX_DATA and + (i - BLOCKSIZE) % MESH_MAX_DATA == 0 + ): + key, _ = meshing(key, iv, sbox=sbox) + plaintext.append(strxor( + ns2block(decrypt(sbox, key, block2ns(data[i:i + BLOCKSIZE]))), + data[i - BLOCKSIZE:i], + )) + if pad: + plaintext[-1] = unpad2(plaintext[-1], BLOCKSIZE) + return b"".join(plaintext) + + +def cnt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX): + """Counter mode of operation + + :param bytes key: encryption key + :param bytes data: plaintext + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :returns: ciphertext + :rtype: bytes + + For decryption you use the same function again. + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + if not data: + raise ValueError("No data supplied") + n2, n1 = encrypt(sbox, key, block2ns(iv)) + gamma = [] + for _ in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE): + n1 = (n1 + C2) % (2 ** 32) + n2 = (n2 + C1) % (2 ** 32 - 1) + gamma.append(ns2block(encrypt(sbox, key, (n1, n2)))) + return strxor(b"".join(gamma), data) + + +MESH_CONST = hexdec("6900722264C904238D3ADB9646E92AC418FEAC9400ED0712C086DCC2EF4CA92B") +MESH_MAX_DATA = 1024 + + +def meshing(key, iv, sbox=DEFAULT_SBOX): + """:rfc:`4357` key meshing + """ + key = ecb_decrypt(key, MESH_CONST, sbox=sbox) + iv = ecb_encrypt(key, iv, sbox=sbox) + return key, iv + + +def cfb_encrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False): + """CFB encryption mode of operation + + :param bytes key: encryption key + :param bytes data: plaintext + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing + :returns: ciphertext + :rtype: bytes + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + if not data: + raise ValueError("No data supplied") + ciphertext = [iv] + for i in xrange(0, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE): + if mesh and i >= MESH_MAX_DATA and i % MESH_MAX_DATA == 0: + key, iv = meshing(key, ciphertext[-1], sbox=sbox) + ciphertext.append(strxor( + data[i:i + BLOCKSIZE], + ns2block(encrypt(sbox, key, block2ns(iv))), + )) + continue + ciphertext.append(strxor( + data[i:i + BLOCKSIZE], + ns2block(encrypt(sbox, key, block2ns(ciphertext[-1]))), + )) + return b"".join(ciphertext[1:]) + + +def cfb_decrypt(key, data, iv=8 * b"\x00", sbox=DEFAULT_SBOX, mesh=False): + """CFB decryption mode of operation + + :param bytes key: encryption key + :param bytes data: plaintext + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + :param bool mesh: enable key meshing + :returns: ciphertext + :rtype: bytes + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + if not data: + raise ValueError("No data supplied") + plaintext = [] + data = iv + data + for i in xrange(BLOCKSIZE, len(data) + pad_size(len(data), BLOCKSIZE), BLOCKSIZE): + if ( + mesh and + (i - BLOCKSIZE) >= MESH_MAX_DATA and + (i - BLOCKSIZE) % MESH_MAX_DATA == 0 + ): + key, iv = meshing(key, data[i - BLOCKSIZE:i], sbox=sbox) + plaintext.append(strxor( + data[i:i + BLOCKSIZE], + ns2block(encrypt(sbox, key, block2ns(iv))), + )) + continue + plaintext.append(strxor( + data[i:i + BLOCKSIZE], + ns2block(encrypt(sbox, key, block2ns(data[i - BLOCKSIZE:i]))), + )) + return b"".join(plaintext) diff --git a/pygost-5.13/pygost/gost28147_mac.py b/pygost-5.13/pygost/gost28147_mac.py new file mode 100644 index 0000000..aab2805 --- /dev/null +++ b/pygost-5.13/pygost/gost28147_mac.py @@ -0,0 +1,99 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST 28147-89 MAC +""" + +from copy import copy + +from pygost.gost28147 import block2ns +from pygost.gost28147 import BLOCKSIZE +from pygost.gost28147 import DEFAULT_SBOX +from pygost.gost28147 import ns2block +from pygost.gost28147 import validate_iv +from pygost.gost28147 import validate_key +from pygost.gost28147 import validate_sbox +from pygost.gost28147 import xcrypt +from pygost.gost3413 import pad1 +from pygost.iface import PEP247 +from pygost.utils import strxor +from pygost.utils import xrange + +digest_size = 8 +SEQ_MAC = ( + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, +) + + +class MAC(PEP247): + """GOST 28147-89 MAC mode of operation + + >>> m = MAC(key=key) + >>> m.update("some data") + >>> m.update("another data") + >>> m.hexdigest()[:8] + 'a687a08b' + """ + digest_size = digest_size + + def __init__(self, key, data=b"", iv=8 * b"\x00", sbox=DEFAULT_SBOX): + """ + :param key: authentication key + :type key: bytes, 32 bytes + :param iv: initialization vector + :type iv: bytes, BLOCKSIZE length + :param sbox: S-box parameters to use + :type sbox: str, SBOXES'es key + """ + validate_key(key) + validate_iv(iv) + validate_sbox(sbox) + self.key = key + self.data = data + self.iv = iv + self.sbox = sbox + + def copy(self): + return MAC(self.key, copy(self.data), self.iv, self.sbox) + + def update(self, data): + """Append data that has to be authenticated + """ + self.data += data + + def digest(self): + """Get MAC tag of supplied data + + You have to provide at least single byte of data. + If you want to produce tag length of 3 bytes, then + ``digest()[:3]``. + """ + if not self.data: + raise ValueError("No data processed") + data = pad1(self.data, BLOCKSIZE) + prev = block2ns(self.iv)[::-1] + for i in xrange(0, len(data), BLOCKSIZE): + prev = xcrypt( + SEQ_MAC, self.sbox, self.key, block2ns(strxor( + data[i:i + BLOCKSIZE], + ns2block(prev), + )), + )[::-1] + return ns2block(prev) + + +def new(key, data=b"", iv=8 * b"\x00", sbox=DEFAULT_SBOX): + return MAC(key, data, iv, sbox) diff --git a/pygost-5.13/pygost/gost3410.py b/pygost-5.13/pygost/gost3410.py new file mode 100644 index 0000000..c12b170 --- /dev/null +++ b/pygost-5.13/pygost/gost3410.py @@ -0,0 +1,412 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST R 34.10 public-key signature function. + +This is implementation of GOST R 34.10-2001 (:rfc:`5832`), GOST R +34.10-2012 (:rfc:`7091`). The difference between 2001 and 2012 is the +key, digest and signature lengths. +""" + +from os import urandom + +from pygost.utils import bytes2long +from pygost.utils import hexdec +from pygost.utils import long2bytes +from pygost.utils import modinvert + + +def point_size(point): + """Determine is it either 256 or 512 bit point + """ + return (512 // 8) if point.bit_length() > 256 else (256 // 8) + + +class GOST3410Curve(object): + """GOST 34.10 validated curve + + >>> curve = CURVES["id-GostR3410-2001-TestParamSet"] + >>> prv = prv_unmarshal(urandom(32)) + >>> signature = sign(curve, prv, GOST341194(data).digest()) + >>> pub = public_key(curve, prv) + >>> verify(curve, pub, GOST341194(data).digest(), signature) + True + + :param long p: characteristic of the underlying prime field + :param long q: elliptic curve subgroup order + :param long a, b: coefficients of the equation of the elliptic curve in + the canonical form + :param long x, y: the coordinate of the point P (generator of the + subgroup of order q) of the elliptic curve in + the canonical form + :param long e, d: coefficients of the equation of the elliptic curve in + the twisted Edwards form + :param str name: human-readable curve name + """ + + def __init__(self, p, q, a, b, x, y, cofactor=1, e=None, d=None, name=None): + self.p = p + self.q = q + self.a = a + self.b = b + self.x = x + self.y = y + self.cofactor = cofactor + self.e = e + self.d = d + if not self.contains((x, y)): + raise ValueError("Invalid parameters") + self._st = None + self.name = name + + @property + def point_size(self): + return point_size(self.p) + + def __repr__(self): + return "<%s: %s>" % (self.__class__.__name__, self.name) + + def pos(self, v): + """Make positive number + """ + if v < 0: + return v + self.p + return v + + def contains(self, point): + """Is point on the curve? + + :type point: (long, long) + """ + x, y = point + r1 = y * y % self.p + r2 = ((x * x + self.a) * x + self.b) % self.p + return r1 == self.pos(r2) + + def _add(self, p1x, p1y, p2x, p2y): + if p1x == p2x and p1y == p2y: + # double + t = ((3 * p1x * p1x + self.a) * modinvert(2 * p1y, self.p)) % self.p + else: + tx = self.pos(p2x - p1x) % self.p + ty = self.pos(p2y - p1y) % self.p + t = (ty * modinvert(tx, self.p)) % self.p + tx = self.pos(t * t - p1x - p2x) % self.p + ty = self.pos(t * (p1x - tx) - p1y) % self.p + return tx, ty + + def exp(self, degree, x=None, y=None): + x = x or self.x + y = y or self.y + tx = x + ty = y + if degree == 0: + raise ValueError("Bad degree value") + degree -= 1 + while degree != 0: + if degree & 1 == 1: + tx, ty = self._add(tx, ty, x, y) + degree = degree >> 1 + x, y = self._add(x, y, x, y) + return tx, ty + + def st(self): + """Compute s/t parameters for twisted Edwards curve points conversion + """ + if self.e is None or self.d is None: + raise ValueError("Non twisted Edwards curve") + if self._st is not None: + return self._st + self._st = ( + self.pos(self.e - self.d) * modinvert(4, self.p) % self.p, + (self.e + self.d) * modinvert(6, self.p) % self.p, + ) + return self._st + + +CURVES = { + "GostR3410_2001_ParamSet_cc": GOST3410Curve( + p=bytes2long(hexdec("C0000000000000000000000000000000000000000000000000000000000003C7")), + q=bytes2long(hexdec("5fffffffffffffffffffffffffffffff606117a2f4bde428b7458a54b6e87b85")), + a=bytes2long(hexdec("C0000000000000000000000000000000000000000000000000000000000003c4")), + b=bytes2long(hexdec("2d06B4265ebc749ff7d0f1f1f88232e81632e9088fd44b7787d5e407e955080c")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000002")), + y=bytes2long(hexdec("a20e034bf8813ef5c18d01105e726a17eb248b264ae9706f440bedc8ccb6b22c")), + ), + "id-GostR3410-2001-TestParamSet": GOST3410Curve( + p=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000431")), + q=bytes2long(hexdec("8000000000000000000000000000000150FE8A1892976154C59CFC193ACCF5B3")), + a=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000007")), + b=bytes2long(hexdec("5FBFF498AA938CE739B8E022FBAFEF40563F6E6A3472FC2A514C0CE9DAE23B7E")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000002")), + y=bytes2long(hexdec("08E2A8A0E65147D4BD6316030E16D19C85C97F0A9CA267122B96ABBCEA7E8FC8")), + ), + "id-tc26-gost-3410-12-256-paramSetA": GOST3410Curve( + p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97")), + q=bytes2long(hexdec("400000000000000000000000000000000FD8CDDFC87B6635C115AF556C360C67")), + a=bytes2long(hexdec("C2173F1513981673AF4892C23035A27CE25E2013BF95AA33B22C656F277E7335")), + b=bytes2long(hexdec("295F9BAE7428ED9CCC20E7C359A9D41A22FCCD9108E17BF7BA9337A6F8AE9513")), + x=bytes2long(hexdec("91E38443A5E82C0D880923425712B2BB658B9196932E02C78B2582FE742DAA28")), + y=bytes2long(hexdec("32879423AB1A0375895786C4BB46E9565FDE0B5344766740AF268ADB32322E5C")), + cofactor=4, + e=0x01, + d=bytes2long(hexdec("0605F6B7C183FA81578BC39CFAD518132B9DF62897009AF7E522C32D6DC7BFFB")), + ), + "id-tc26-gost-3410-12-256-paramSetB": GOST3410Curve( + p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97")), + q=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893")), + a=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94")), + b=bytes2long(hexdec("00000000000000000000000000000000000000000000000000000000000000a6")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000001")), + y=bytes2long(hexdec("8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14")), + ), + "id-tc26-gost-3410-12-256-paramSetC": GOST3410Curve( + p=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000C99")), + q=bytes2long(hexdec("800000000000000000000000000000015F700CFFF1A624E5E497161BCC8A198F")), + a=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000C96")), + b=bytes2long(hexdec("3E1AF419A269A5F866A7D3C25C3DF80AE979259373FF2B182F49D4CE7E1BBC8B")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000001")), + y=bytes2long(hexdec("3FA8124359F96680B83D1C3EB2C070E5C545C9858D03ECFB744BF8D717717EFC")), + ), + "id-tc26-gost-3410-12-256-paramSetD": GOST3410Curve( + p=bytes2long(hexdec("9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D759B")), + q=bytes2long(hexdec("9B9F605F5A858107AB1EC85E6B41C8AA582CA3511EDDFB74F02F3A6598980BB9")), + a=bytes2long(hexdec("9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D7598")), + b=bytes2long(hexdec("000000000000000000000000000000000000000000000000000000000000805a")), + x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000000")), + y=bytes2long(hexdec("41ECE55743711A8C3CBF3783CD08C0EE4D4DC440D4641A8F366E550DFDB3BB67")), + ), + "id-tc26-gost-3410-12-512-paramSetTest": GOST3410Curve( + p=bytes2long(hexdec("4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DF1D852741AF4704A0458047E80E4546D35B8336FAC224DD81664BBF528BE6373")), + q=bytes2long(hexdec("4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DA82F2D7ECB1DBAC719905C5EECC423F1D86E25EDBE23C595D644AAF187E6E6DF")), + a=7, + b=bytes2long(hexdec("1CFF0806A31116DA29D8CFA54E57EB748BC5F377E49400FDD788B649ECA1AC4361834013B2AD7322480A89CA58E0CF74BC9E540C2ADD6897FAD0A3084F302ADC")), + x=bytes2long(hexdec("24D19CC64572EE30F396BF6EBBFD7A6C5213B3B3D7057CC825F91093A68CD762FD60611262CD838DC6B60AA7EEE804E28BC849977FAC33B4B530F1B120248A9A")), + y=bytes2long(hexdec("2BB312A43BD2CE6E0D020613C857ACDDCFBF061E91E5F2C3F32447C259F39B2C83AB156D77F1496BF7EB3351E1EE4E43DC1A18B91B24640B6DBB92CB1ADD371E")), + ), + "id-tc26-gost-3410-12-512-paramSetA": GOST3410Curve( + p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7")), + q=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275")), + a=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4")), + b=bytes2long(hexdec("E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760")), + x=bytes2long(hexdec("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003")), + y=bytes2long(hexdec("7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4")), + ), + "id-tc26-gost-3410-12-512-paramSetB": GOST3410Curve( + p=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F")), + q=bytes2long(hexdec("800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD")), + a=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C")), + b=bytes2long(hexdec("687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116")), + x=bytes2long(hexdec("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002")), + y=bytes2long(hexdec("1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD")), + ), + "id-tc26-gost-3410-12-512-paramSetC": GOST3410Curve( + p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7")), + q=bytes2long(hexdec("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC98CDBA46506AB004C33A9FF5147502CC8EDA9E7A769A12694623CEF47F023ED")), + a=bytes2long(hexdec("DC9203E514A721875485A529D2C722FB187BC8980EB866644DE41C68E143064546E861C0E2C9EDD92ADE71F46FCF50FF2AD97F951FDA9F2A2EB6546F39689BD3")), + b=bytes2long(hexdec("B4C4EE28CEBC6C2C8AC12952CF37F16AC7EFB6A9F69F4B57FFDA2E4F0DE5ADE038CBC2FFF719D2C18DE0284B8BFEF3B52B8CC7A5F5BF0A3C8D2319A5312557E1")), + x=bytes2long(hexdec("E2E31EDFC23DE7BDEBE241CE593EF5DE2295B7A9CBAEF021D385F7074CEA043AA27272A7AE602BF2A7B9033DB9ED3610C6FB85487EAE97AAC5BC7928C1950148")), + y=bytes2long(hexdec("F5CE40D95B5EB899ABBCCFF5911CB8577939804D6527378B8C108C3D2090FF9BE18E2D33E3021ED2EF32D85822423B6304F726AA854BAE07D0396E9A9ADDC40F")), + cofactor=4, + e=0x01, + d=bytes2long(hexdec("9E4F5D8C017D8D9F13A5CF3CDF5BFE4DAB402D54198E31EBDE28A0621050439CA6B39E0A515C06B304E2CE43E79E369E91A0CFC2BC2A22B4CA302DBB33EE7550")), + ), +} +CURVES["id-GostR3410-2001-CryptoPro-A-ParamSet"] = CURVES["id-tc26-gost-3410-12-256-paramSetB"] +CURVES["id-GostR3410-2001-CryptoPro-B-ParamSet"] = CURVES["id-tc26-gost-3410-12-256-paramSetC"] +CURVES["id-GostR3410-2001-CryptoPro-C-ParamSet"] = CURVES["id-tc26-gost-3410-12-256-paramSetD"] +CURVES["id-GostR3410-2001-CryptoPro-XchA-ParamSet"] = CURVES["id-GostR3410-2001-CryptoPro-A-ParamSet"] +CURVES["id-GostR3410-2001-CryptoPro-XchB-ParamSet"] = CURVES["id-GostR3410-2001-CryptoPro-C-ParamSet"] +CURVES["id-tc26-gost-3410-2012-256-paramSetA"] = CURVES["id-tc26-gost-3410-12-256-paramSetA"] +CURVES["id-tc26-gost-3410-2012-256-paramSetB"] = CURVES["id-tc26-gost-3410-12-256-paramSetB"] +CURVES["id-tc26-gost-3410-2012-256-paramSetC"] = CURVES["id-tc26-gost-3410-12-256-paramSetC"] +CURVES["id-tc26-gost-3410-2012-256-paramSetD"] = CURVES["id-tc26-gost-3410-12-256-paramSetD"] +CURVES["id-tc26-gost-3410-2012-512-paramSetTest"] = CURVES["id-tc26-gost-3410-12-512-paramSetTest"] +CURVES["id-tc26-gost-3410-2012-512-paramSetA"] = CURVES["id-tc26-gost-3410-12-512-paramSetA"] +CURVES["id-tc26-gost-3410-2012-512-paramSetB"] = CURVES["id-tc26-gost-3410-12-512-paramSetB"] +CURVES["id-tc26-gost-3410-2012-512-paramSetC"] = CURVES["id-tc26-gost-3410-12-512-paramSetC"] +for _name, _curve in CURVES.items(): + _curve.name = _name +DEFAULT_CURVE = CURVES["id-tc26-gost-3410-12-256-paramSetB"] + + +def public_key(curve, prv, mask=None): + """Generate public key from the private one + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :returns: public key's parts, X and Y + :rtype: (long, long) + """ + pub = curve.exp(prv) + if mask is not None: + pub = curve.exp(mask, pub[0], pub[1]) + return pub + + +def sign(curve, prv, digest, rand=None, mask=None): + """Calculate signature for provided digest + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param digest: digest for signing + :type digest: bytes, 32 or 64 bytes + :param rand: optional predefined random data used for k/r generation + :type rand: bytes, 32 or 64 bytes + :returns: signature, BE(S) || BE(R) + :rtype: bytes, 64 or 128 bytes + """ + size = curve.point_size + q = curve.q + e = bytes2long(digest) % q + if e == 0: + e = 1 + while True: + if rand is None: + rand = urandom(size) + elif len(rand) != size: + raise ValueError("rand length != %d" % size) + k = bytes2long(rand) % q + if k == 0: + continue + r, y = curve.exp(k) + if mask is not None: + r, y = curve.exp(mask, x=r, y=y) + r %= q + if r == 0: + continue + d = prv * r + k *= e + s = d + k + if mask is not None: + s *= mask + s %= q + if s == 0: + continue + break + return long2bytes(s, size) + long2bytes(r, size) + + +def verify(curve, pub, digest, signature): + """Verify provided digest with the signature + + :param GOST3410Curve curve: curve to use + :type pub: (long, long) + :param digest: digest needed to check + :type digest: bytes, 32 or 64 bytes + :param signature: signature to verify with + :type signature: bytes, 64 or 128 bytes + :rtype: bool + """ + size = curve.point_size + if len(signature) != size * 2: + raise ValueError("Invalid signature length") + q = curve.q + p = curve.p + s = bytes2long(signature[:size]) + r = bytes2long(signature[size:]) + if r <= 0 or r >= q or s <= 0 or s >= q: + return False + e = bytes2long(digest) % curve.q + if e == 0: + e = 1 + v = modinvert(e, q) + z1 = s * v % q + z2 = q - r * v % q + p1x, p1y = curve.exp(z1) + q1x, q1y = curve.exp(z2, pub[0], pub[1]) + lm = q1x - p1x + if lm < 0: + lm += p + lm = modinvert(lm, p) + z1 = q1y - p1y + lm = lm * z1 % p + lm = lm * lm % p + lm = lm - p1x - q1x + lm = lm % p + if lm < 0: + lm += p + lm %= q + # This is not constant time comparison! + return lm == r + + +def prv_unmarshal(prv): + """Unmarshal little-endian private key + + :param bytes prv: serialized private key + :rtype: long + + It is advisable to use :py:func:`pygost.gost3410.prv_marshal` to + assure that key i in curve's Q field for better compatibility with + some implementations. + """ + return bytes2long(prv[::-1]) + + +def prv_marshal(curve, prv): + """Marshal little-endian private key + + :param GOST3410Curve curve: curve to use + :param long prv: serialized private key + :rtype: bytes + + Key is in curve's Q field. + """ + return long2bytes(prv % curve.q, point_size(prv))[::-1] + + +def pub_marshal(pub): + """Marshal public key + + :type pub: (long, long) + :rtype: bytes + :returns: LE(X) || LE(Y) + """ + size = point_size(pub[0]) + return (long2bytes(pub[1], size) + long2bytes(pub[0], size))[::-1] + + +def pub_unmarshal(pub): + """Unmarshal public key + + :param pub: LE(X) || LE(Y) + :type pub: bytes + :rtype: (long, long) + """ + size = len(pub) // 2 + pub = pub[::-1] + return (bytes2long(pub[size:]), bytes2long(pub[:size])) + + +def uv2xy(curve, u, v): + """Convert twisted Edwards curve U,V coordinates to Weierstrass X,Y + """ + s, t = curve.st() + k1 = (s * (1 + v)) % curve.p + k2 = curve.pos(1 - v) + x = t + k1 * modinvert(k2, curve.p) + y = k1 * modinvert(u * k2, curve.p) + return x % curve.p, y % curve.p + + +def xy2uv(curve, x, y): + """Convert Weierstrass X,Y coordinates to twisted Edwards curve U,V + """ + s, t = curve.st() + xmt = curve.pos(x - t) + u = xmt * modinvert(y, curve.p) + v = curve.pos(xmt - s) * modinvert(xmt + s, curve.p) + return u % curve.p, v % curve.p diff --git a/pygost-5.13/pygost/gost3410_vko.py b/pygost-5.13/pygost/gost3410_vko.py new file mode 100644 index 0000000..3f169dc --- /dev/null +++ b/pygost-5.13/pygost/gost3410_vko.py @@ -0,0 +1,97 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Key agreement functions, VKO GOST R 34.10-2001/2012 +""" + +from pygost.gost3410 import pub_marshal +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.gost341194 import GOST341194 +from pygost.utils import bytes2long + + +def ukm_unmarshal(ukm): + """Unmarshal UKM value + + :type ukm: little-endian bytes + :rtype: long + """ + return bytes2long(ukm[::-1]) + + +def kek(curve, prv, pub, ukm, mask=None): + if not curve.contains(pub): + raise ValueError("pub is not on the curve") + key = curve.exp(prv, pub[0], pub[1]) + key = curve.exp(curve.cofactor * ukm, key[0], key[1]) + if mask is not None: + key = curve.exp(mask, key[0], key[1]) + return pub_marshal(key) + + +def kek_34102001(curve, prv, pub, ukm): + """Key agreement (34.10-2001, 34.11-94) + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param pub: public key + :type pub: (long, long) + :param long ukm: user keying material, VKO-factor + :returns: Key Encryption Key (shared key) + :rtype: bytes, 32 bytes + + Shared Key Encryption Key computation is based on + :rfc:`4357` VKO GOST R 34.10-2001 with little-endian + hash output. + """ + return GOST341194( + kek(curve, prv, pub, ukm), + sbox="id-GostR3411-94-CryptoProParamSet", + ).digest() + + +def kek_34102012256(curve, prv, pub, ukm=1): + """Key agreement (34.10-2012, 34.11-2012 256 bit) + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param pub: public key + :type pub: (long, long) + :param long ukm: user keying material, VKO-factor + :returns: Key Encryption Key (shared key) + :rtype: bytes, 32 bytes + + Shared Key Encryption Key computation is based on + :rfc:`7836` VKO GOST R 34.10-2012. + """ + return GOST34112012256(kek(curve, prv, pub, ukm)).digest() + + +def kek_34102012512(curve, prv, pub, ukm=1): + """Key agreement (34.10-2012, 34.11-2012 512 bit) + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param pub: public key + :type pub: (long, long) + :param long ukm: user keying material, VKO-factor + :returns: Key Encryption Key (shared key) + :rtype: bytes, 32 bytes + + Shared Key Encryption Key computation is based on + :rfc:`7836` VKO GOST R 34.10-2012. + """ + return GOST34112012512(kek(curve, prv, pub, ukm)).digest() diff --git a/pygost-5.13/pygost/gost34112012.py b/pygost-5.13/pygost/gost34112012.py new file mode 100644 index 0000000..91782de --- /dev/null +++ b/pygost-5.13/pygost/gost34112012.py @@ -0,0 +1,299 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST R 34.11-2012 (Streebog) hash function common files + +This is implementation of :rfc:`6986`. Most function and variable names are +taken according to specification's terminology. +""" + +from copy import copy +from struct import pack +from struct import unpack + +from pygost.iface import PEP247 +from pygost.utils import hexdec +from pygost.utils import strxor +from pygost.utils import xrange + + +BLOCKSIZE = 64 +Pi = bytearray(( + 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, + 218, 35, 197, 4, 77, 233, 119, 240, 219, 147, 46, + 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, 249, + 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, + 139, 1, 142, 79, 5, 132, 2, 174, 227, 106, 143, + 160, 6, 11, 237, 152, 127, 212, 211, 31, 235, 52, + 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, + 58, 206, 204, 181, 112, 14, 86, 8, 12, 118, 18, + 191, 114, 19, 71, 156, 183, 93, 135, 21, 161, 150, + 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, + 178, 177, 50, 117, 25, 61, 255, 53, 138, 126, 109, + 84, 198, 128, 195, 189, 13, 87, 223, 245, 36, 169, + 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, + 3, 224, 15, 236, 222, 122, 148, 176, 188, 220, 232, + 40, 80, 78, 51, 10, 74, 167, 151, 96, 115, 30, + 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, + 173, 69, 70, 146, 39, 94, 85, 47, 140, 163, 165, + 125, 105, 213, 149, 59, 7, 88, 179, 64, 134, 172, + 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, 225, + 27, 131, 73, 76, 63, 248, 254, 141, 83, 170, 144, + 202, 216, 133, 97, 32, 113, 103, 164, 45, 43, 9, + 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166, + 116, 210, 230, 244, 180, 192, 209, 102, 175, 194, 57, + 75, 99, 182, +)) + +A = [unpack(">Q", hexdec(s))[0] for s in ( + "8e20faa72ba0b470", "47107ddd9b505a38", "ad08b0e0c3282d1c", "d8045870ef14980e", + "6c022c38f90a4c07", "3601161cf205268d", "1b8e0b0e798c13c8", "83478b07b2468764", + "a011d380818e8f40", "5086e740ce47c920", "2843fd2067adea10", "14aff010bdd87508", + "0ad97808d06cb404", "05e23c0468365a02", "8c711e02341b2d01", "46b60f011a83988e", + "90dab52a387ae76f", "486dd4151c3dfdb9", "24b86a840e90f0d2", "125c354207487869", + "092e94218d243cba", "8a174a9ec8121e5d", "4585254f64090fa0", "accc9ca9328a8950", + "9d4df05d5f661451", "c0a878a0a1330aa6", "60543c50de970553", "302a1e286fc58ca7", + "18150f14b9ec46dd", "0c84890ad27623e0", "0642ca05693b9f70", "0321658cba93c138", + "86275df09ce8aaa8", "439da0784e745554", "afc0503c273aa42a", "d960281e9d1d5215", + "e230140fc0802984", "71180a8960409a42", "b60c05ca30204d21", "5b068c651810a89e", + "456c34887a3805b9", "ac361a443d1c8cd2", "561b0d22900e4669", "2b838811480723ba", + "9bcf4486248d9f5d", "c3e9224312c8c1a0", "effa11af0964ee50", "f97d86d98a327728", + "e4fa2054a80b329c", "727d102a548b194e", "39b008152acb8227", "9258048415eb419d", + "492c024284fbaec0", "aa16012142f35760", "550b8e9e21f7a530", "a48b474f9ef5dc18", + "70a6a56e2440598e", "3853dc371220a247", "1ca76e95091051ad", "0edd37c48a08a6d8", + "07e095624504536c", "8d70c431ac02a736", "c83862965601dd1b", "641c314b2b8ee083", +)] + +Tau = ( + 0, 8, 16, 24, 32, 40, 48, 56, + 1, 9, 17, 25, 33, 41, 49, 57, + 2, 10, 18, 26, 34, 42, 50, 58, + 3, 11, 19, 27, 35, 43, 51, 59, + 4, 12, 20, 28, 36, 44, 52, 60, + 5, 13, 21, 29, 37, 45, 53, 61, + 6, 14, 22, 30, 38, 46, 54, 62, + 7, 15, 23, 31, 39, 47, 55, 63, +) + +C = [hexdec("".join(s))[::-1] for s in ( + ( + "b1085bda1ecadae9ebcb2f81c0657c1f", + "2f6a76432e45d016714eb88d7585c4fc", + "4b7ce09192676901a2422a08a460d315", + "05767436cc744d23dd806559f2a64507", + ), + ( + "6fa3b58aa99d2f1a4fe39d460f70b5d7", + "f3feea720a232b9861d55e0f16b50131", + "9ab5176b12d699585cb561c2db0aa7ca", + "55dda21bd7cbcd56e679047021b19bb7", + ), + ( + "f574dcac2bce2fc70a39fc286a3d8435", + "06f15e5f529c1f8bf2ea7514b1297b7b", + "d3e20fe490359eb1c1c93a376062db09", + "c2b6f443867adb31991e96f50aba0ab2", + ), + ( + "ef1fdfb3e81566d2f948e1a05d71e4dd", + "488e857e335c3c7d9d721cad685e353f", + "a9d72c82ed03d675d8b71333935203be", + "3453eaa193e837f1220cbebc84e3d12e", + ), + ( + "4bea6bacad4747999a3f410c6ca92363", + "7f151c1f1686104a359e35d7800fffbd", + "bfcd1747253af5a3dfff00b723271a16", + "7a56a27ea9ea63f5601758fd7c6cfe57", + ), + ( + "ae4faeae1d3ad3d96fa4c33b7a3039c0", + "2d66c4f95142a46c187f9ab49af08ec6", + "cffaa6b71c9ab7b40af21f66c2bec6b6", + "bf71c57236904f35fa68407a46647d6e", + ), + ( + "f4c70e16eeaac5ec51ac86febf240954", + "399ec6c7e6bf87c9d3473e33197a93c9", + "0992abc52d822c3706476983284a0504", + "3517454ca23c4af38886564d3a14d493", + ), + ( + "9b1f5b424d93c9a703e7aa020c6e4141", + "4eb7f8719c36de1e89b4443b4ddbc49a", + "f4892bcb929b069069d18d2bd1a5c42f", + "36acc2355951a8d9a47f0dd4bf02e71e", + ), + ( + "378f5a541631229b944c9ad8ec165fde", + "3a7d3a1b258942243cd955b7e00d0984", + "800a440bdbb2ceb17b2b8a9aa6079c54", + "0e38dc92cb1f2a607261445183235adb", + ), + ( + "abbedea680056f52382ae548b2e4f3f3", + "8941e71cff8a78db1fffe18a1b336103", + "9fe76702af69334b7a1e6c303b7652f4", + "3698fad1153bb6c374b4c7fb98459ced", + ), + ( + "7bcd9ed0efc889fb3002c6cd635afe94", + "d8fa6bbbebab07612001802114846679", + "8a1d71efea48b9caefbacd1d7d476e98", + "dea2594ac06fd85d6bcaa4cd81f32d1b", + ), + ( + "378ee767f11631bad21380b00449b17a", + "cda43c32bcdf1d77f82012d430219f9b", + "5d80ef9d1891cc86e71da4aa88e12852", + "faf417d5d9b21b9948bc924af11bd720", + ), +)] + + +def _lcache(): + cache = [] + for byteN in xrange(8): + cache.append([0 for _ in xrange(256)]) + for byteN in xrange(8): + for byteVal in xrange(256): + res64 = 0 + val = byteVal + for bitN in xrange(8): + if val & 0x80 > 0: + res64 ^= A[(7 - byteN) * 8 + bitN] + val <<= 1 + cache[byteN][byteVal] = res64 + return cache + + +# Trade memory for CPU for part of L() calculations +LCache = _lcache() + + +def add512bit(a, b): + a = int.from_bytes(a, "little") + b = int.from_bytes(b, "little") + r = (a + b) % (1 << 512) + return r.to_bytes(512 // 8, "little") + + +def g(n, hsh, msg): + res = E(LPS(strxor(hsh[:8], pack(">> m = GOST34112012(digest_size=32) + >>> m.update("foo") + >>> m.update("bar") + >>> m.hexdigest() + 'e3c9fd89226d93b489a9fe27d686806e24a514e3787bca053c698ec4616ceb78' + """ + block_size = BLOCKSIZE + + def __init__(self, data=b"", digest_size=64): + """ + :param digest_size: hash digest size to compute + :type digest_size: 32 or 64 bytes + """ + self._digest_size = digest_size + self.hsh = BLOCKSIZE * (b"\x01" if digest_size == 32 else b"\x00") + self.chk = bytearray(BLOCKSIZE * b"\x00") + self.n = 0 + self.buf = b"" + self.update(data) + + def copy(self): + obj = GOST34112012() + obj._digest_size = self._digest_size + obj.hsh = self.hsh + obj.chk = copy(self.chk) + obj.n = self.n + obj.buf = self.buf + return obj + + @property + def digest_size(self): + return self._digest_size + + def _update_block(self, block): + self.hsh = g(self.n, self.hsh, block) + self.chk = add512bit(self.chk, block) + self.n += 512 + + def update(self, data): + """Update state with the new data + """ + if len(self.buf) > 0: + chunk_len = BLOCKSIZE - len(self.buf) + self.buf += data[:chunk_len] + data = data[chunk_len:] + if len(self.buf) == BLOCKSIZE: + self._update_block(self.buf) + self.buf = b"" + while len(data) >= BLOCKSIZE: + self._update_block(data[:BLOCKSIZE]) + data = data[BLOCKSIZE:] + self.buf += data + + def digest(self): + """Get hash of the provided data + """ + data = self.buf + + # Padding + padblock_size = len(data) * 8 + data += b"\x01" + padlen = BLOCKSIZE - len(data) + if padlen != BLOCKSIZE: + data += b"\x00" * padlen + + hsh = g(self.n, self.hsh, data) + n = self.n + padblock_size + chk = add512bit(self.chk, data) + hsh = g(0, hsh, pack(" +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST R 34.11-94 hash function + +This is implementation of :rfc:`5831`. Most function and variable names are +taken according to specification's terminology. +""" + +from copy import copy +from functools import partial +from struct import pack + +from pygost.gost28147 import block2ns +from pygost.gost28147 import encrypt +from pygost.gost28147 import ns2block +from pygost.gost28147 import validate_sbox +from pygost.iface import PEP247 +from pygost.pbkdf2 import pbkdf2 as pbkdf2_base +from pygost.utils import hexdec +from pygost.utils import hexenc +from pygost.utils import strxor +from pygost.utils import xrange + + +DEFAULT_SBOX = "id-GostR3411-94-CryptoProParamSet" +BLOCKSIZE = 32 +C2 = 32 * b"\x00" +C3 = hexdec(b"ff00ffff000000ffff0000ff00ffff0000ff00ff00ff00ffff00ff00ff00ff00") +C4 = 32 * b"\x00" +digest_size = 32 + + +def A(x): + x4, x3, x2, x1 = x[0:8], x[8:16], x[16:24], x[24:32] + return b"".join((strxor(x1, x2), x4, x3, x2)) + + +def P(x): + return bytearray(( + x[0], x[8], x[16], x[24], x[1], x[9], x[17], x[25], x[2], + x[10], x[18], x[26], x[3], x[11], x[19], x[27], x[4], x[12], + x[20], x[28], x[5], x[13], x[21], x[29], x[6], x[14], x[22], + x[30], x[7], x[15], x[23], x[31], + )) + + +def _chi(Y): + """Chi function + + This is some kind of LFSR. + """ + (y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2, y1) = ( + Y[0:2], Y[2:4], Y[4:6], Y[6:8], Y[8:10], Y[10:12], Y[12:14], + Y[14:16], Y[16:18], Y[18:20], Y[20:22], Y[22:24], Y[24:26], + Y[26:28], Y[28:30], Y[30:32], + ) + by1, by2, by3, by4, by13, by16, byx = ( + bytearray(y1), bytearray(y2), bytearray(y3), bytearray(y4), + bytearray(y13), bytearray(y16), bytearray(2), + ) + byx[0] = by1[0] ^ by2[0] ^ by3[0] ^ by4[0] ^ by13[0] ^ by16[0] + byx[1] = by1[1] ^ by2[1] ^ by3[1] ^ by4[1] ^ by13[1] ^ by16[1] + return b"".join(( + bytes(byx), y16, y15, y14, y13, y12, y11, y10, y9, y8, y7, y6, y5, y4, y3, y2 + )) + + +def _step(hin, m, sbox): + """Step function + + H_out = f(H_in, m) + """ + # Generate keys + u = hin + v = m + w = strxor(hin, m) + k1 = P(w) + + u = strxor(A(u), C2) + v = A(A(v)) + w = strxor(u, v) + k2 = P(w) + + u = strxor(A(u), C3) + v = A(A(v)) + w = strxor(u, v) + k3 = P(w) + + u = strxor(A(u), C4) + v = A(A(v)) + w = strxor(u, v) + k4 = P(w) + + # Encipher + h4, h3, h2, h1 = hin[0:8], hin[8:16], hin[16:24], hin[24:32] + s1 = ns2block(encrypt(sbox, k1[::-1], block2ns(h1[::-1])))[::-1] + s2 = ns2block(encrypt(sbox, k2[::-1], block2ns(h2[::-1])))[::-1] + s3 = ns2block(encrypt(sbox, k3[::-1], block2ns(h3[::-1])))[::-1] + s4 = ns2block(encrypt(sbox, k4[::-1], block2ns(h4[::-1])))[::-1] + s = b"".join((s4, s3, s2, s1)) + + # Permute + # H_out = chi^61(H_in XOR chi(m XOR chi^12(S))) + x = s + for _ in xrange(12): + x = _chi(x) + x = strxor(x, m) + x = _chi(x) + x = strxor(hin, x) + for _ in xrange(61): + x = _chi(x) + return x + + +class GOST341194(PEP247): + """GOST 34.11-94 big-endian hash + + >>> m = GOST341194() + >>> m.update("foo") + >>> m.update("bar") + >>> m.hexdigest() + '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af' + >>> GOST341194("foobar").hexdigest() + '3bd8a3a35917871dfa0d49f9e73e7c57eea028dc061133eb560849ea20c133af' + """ + block_size = BLOCKSIZE + digest_size = digest_size + + def __init__(self, data=b"", sbox=DEFAULT_SBOX): + """ + :param bytes data: provide initial data + :param bytes sbox: S-box to use + """ + validate_sbox(sbox) + self.data = data + self.sbox = sbox + + def copy(self): + return GOST341194(copy(self.data), self.sbox) + + def update(self, data): + """Append data that has to be hashed + """ + self.data += data + + def digest(self): + """Get hash of the provided data + """ + _len = 0 + checksum = 0 + h = 32 * b"\x00" + m = self.data + for i in xrange(0, len(m), BLOCKSIZE): + part = m[i:i + BLOCKSIZE][::-1] + _len += len(part) * 8 + checksum = (checksum + int(hexenc(part), 16)) % (2 ** 256) + if len(part) < BLOCKSIZE: + part = b"\x00" * (BLOCKSIZE - len(part)) + part + h = _step(h, part, self.sbox) + h = _step(h, 24 * b"\x00" + pack(">Q", _len), self.sbox) + + checksum = hex(checksum)[2:].rstrip("L") + if len(checksum) % 2 != 0: + checksum = "0" + checksum + checksum = hexdec(checksum) + checksum = b"\x00" * (BLOCKSIZE - len(checksum)) + checksum + h = _step(h, checksum, self.sbox) + return h[::-1] + + +def new(data=b"", sbox=DEFAULT_SBOX): + return GOST341194(data, sbox) + + +PBKDF2_HASHER = partial(GOST341194, sbox="id-GostR3411-94-CryptoProParamSet") + + +def pbkdf2(password, salt, iterations, dklen): + return pbkdf2_base(PBKDF2_HASHER, password, salt, iterations, dklen) diff --git a/pygost-5.13/pygost/gost3412.py b/pygost-5.13/pygost/gost3412.py new file mode 100644 index 0000000..b9472ee --- /dev/null +++ b/pygost-5.13/pygost/gost3412.py @@ -0,0 +1,186 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST 34.12-2015 64 and 128 bit block ciphers (:rfc:`7801`) + +Several precalculations are performed during this module importing. +""" + +from pygost.gost28147 import block2ns as gost28147_block2ns +from pygost.gost28147 import decrypt as gost28147_decrypt +from pygost.gost28147 import encrypt as gost28147_encrypt +from pygost.gost28147 import ns2block as gost28147_ns2block +from pygost.utils import strxor +from pygost.utils import xrange + + +KEYSIZE = 32 + +LC = bytearray(( + 148, 32, 133, 16, 194, 192, 1, 251, 1, 192, 194, 16, 133, 32, 148, 1, +)) +PI = bytearray(( + 252, 238, 221, 17, 207, 110, 49, 22, 251, 196, 250, 218, 35, 197, 4, 77, + 233, 119, 240, 219, 147, 46, 153, 186, 23, 54, 241, 187, 20, 205, 95, 193, + 249, 24, 101, 90, 226, 92, 239, 33, 129, 28, 60, 66, 139, 1, 142, 79, 5, + 132, 2, 174, 227, 106, 143, 160, 6, 11, 237, 152, 127, 212, 211, 31, 235, + 52, 44, 81, 234, 200, 72, 171, 242, 42, 104, 162, 253, 58, 206, 204, 181, + 112, 14, 86, 8, 12, 118, 18, 191, 114, 19, 71, 156, 183, 93, 135, 21, 161, + 150, 41, 16, 123, 154, 199, 243, 145, 120, 111, 157, 158, 178, 177, 50, 117, + 25, 61, 255, 53, 138, 126, 109, 84, 198, 128, 195, 189, 13, 87, 223, 245, + 36, 169, 62, 168, 67, 201, 215, 121, 214, 246, 124, 34, 185, 3, 224, 15, + 236, 222, 122, 148, 176, 188, 220, 232, 40, 80, 78, 51, 10, 74, 167, 151, + 96, 115, 30, 0, 98, 68, 26, 184, 56, 130, 100, 159, 38, 65, 173, 69, 70, + 146, 39, 94, 85, 47, 140, 163, 165, 125, 105, 213, 149, 59, 7, 88, 179, 64, + 134, 172, 29, 247, 48, 55, 107, 228, 136, 217, 231, 137, 225, 27, 131, 73, + 76, 63, 248, 254, 141, 83, 170, 144, 202, 216, 133, 97, 32, 113, 103, 164, + 45, 43, 9, 91, 203, 155, 37, 208, 190, 229, 108, 82, 89, 166, 116, 210, 230, + 244, 180, 192, 209, 102, 175, 194, 57, 75, 99, 182, +)) + +######################################################################## +# Precalculate inverted PI value as a performance optimization. +# Actually it can be computed only once and saved on the disk. +######################################################################## +PIinv = bytearray(256) +for x in xrange(256): + PIinv[PI[x]] = x + + +def gf(a, b): + c = 0 + while b: + if b & 1: + c ^= a + if a & 0x80: + a = (a << 1) ^ 0x1C3 + else: + a <<= 1 + b >>= 1 + return c + +######################################################################## +# Precalculate all possible gf(byte, byte) values as a performance +# optimization. +# Actually it can be computed only once and saved on the disk. +######################################################################## + + +GF = [bytearray(256) for _ in xrange(256)] + +for x in xrange(256): + for y in xrange(256): + GF[x][y] = gf(x, y) + + +def L(blk, rounds=16): + for _ in range(rounds): + t = blk[15] + for i in range(14, -1, -1): + blk[i + 1] = blk[i] + t ^= GF[blk[i]][LC[i]] + blk[0] = t + return blk + + +def Linv(blk): + for _ in range(16): + t = blk[0] + for i in range(15): + blk[i] = blk[i + 1] + t ^= GF[blk[i]][LC[i]] + blk[15] = t + return blk + +######################################################################## +# Precalculate values of the C -- it does not depend on key. +# Actually it can be computed only once and saved on the disk. +######################################################################## + + +C = [] + +for x in range(1, 33): + y = bytearray(16) + y[15] = x + C.append(L(y)) + + +def lp(blk): + return L([PI[v] for v in blk]) + + +class GOST3412Kuznechik(object): + """GOST 34.12-2015 128-bit block cipher Кузнечик (Kuznechik) + """ + blocksize = 16 + + def __init__(self, key): + """ + :param key: encryption/decryption key + :type key: bytes, 32 bytes + + Key scheduling (roundkeys precomputation) is performed here. + """ + kr0 = bytearray(key[:16]) + kr1 = bytearray(key[16:]) + self.ks = [kr0, kr1] + for i in range(4): + for j in range(8): + k = lp(bytearray(strxor(C[8 * i + j], kr0))) + kr0, kr1 = [strxor(k, kr1), kr0] + self.ks.append(kr0) + self.ks.append(kr1) + + def encrypt(self, blk): + blk = bytearray(blk) + for i in range(9): + blk = lp(bytearray(strxor(self.ks[i], blk))) + return bytes(strxor(self.ks[9], blk)) + + def decrypt(self, blk): + blk = bytearray(blk) + for i in range(9, 0, -1): + blk = [PIinv[v] for v in Linv(bytearray(strxor(self.ks[i], blk)))] + return bytes(strxor(self.ks[0], blk)) + + +class GOST3412Magma(object): + """GOST 34.12-2015 64-bit block cipher Магма (Magma) + """ + blocksize = 8 + + def __init__(self, key): + """ + :param key: encryption/decryption key + :type key: bytes, 32 bytes + """ + # Backward compatibility key preparation for 28147-89 key schedule + self.key = b"".join(key[i * 4:i * 4 + 4][::-1] for i in range(8)) + self.sbox = "id-tc26-gost-28147-param-Z" + + def encrypt(self, blk): + return gost28147_ns2block(gost28147_encrypt( + self.sbox, + self.key, + gost28147_block2ns(blk[::-1]), + ))[::-1] + + def decrypt(self, blk): + return gost28147_ns2block(gost28147_decrypt( + self.sbox, + self.key, + gost28147_block2ns(blk[::-1]), + ))[::-1] diff --git a/pygost-5.13/pygost/gost3413.py b/pygost-5.13/pygost/gost3413.py new file mode 100644 index 0000000..f3cb5da --- /dev/null +++ b/pygost-5.13/pygost/gost3413.py @@ -0,0 +1,392 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""GOST R 34.13-2015: Modes of operation for block ciphers + +This module currently includes only padding methods. +""" + +from os import urandom + +from pygost.utils import bytes2long +from pygost.utils import long2bytes +from pygost.utils import strxor +from pygost.utils import xrange + + +KEYSIZE = 32 + + +def pad_size(data_size, blocksize): + """Calculate required pad size to full up blocksize + """ + if data_size < blocksize: + return blocksize - data_size + if data_size % blocksize == 0: + return 0 + return blocksize - data_size % blocksize + + +def pad1(data, blocksize): + """Padding method 1 + + Just fill up with zeros if necessary. + """ + return data + b"\x00" * pad_size(len(data), blocksize) + + +def pad2(data, blocksize): + """Padding method 2 (also known as ISO/IEC 7816-4) + + Add one bit and then fill up with zeros. + """ + return data + b"\x80" + b"\x00" * pad_size(len(data) + 1, blocksize) + + +def unpad2(data, blocksize): + """Unpad method 2 + """ + last_block = bytearray(data[-blocksize:]) + pad_index = last_block.rfind(b"\x80") + if pad_index == -1: + raise ValueError("Invalid padding") + for c in last_block[pad_index + 1:]: + if c != 0: + raise ValueError("Invalid padding") + return data[:-(blocksize - pad_index)] + + +def pad3(data, blocksize): + """Padding method 3 + """ + if pad_size(len(data), blocksize) == 0: + return data + return pad2(data, blocksize) + + +def ecb_encrypt(encrypter, bs, pt): + """ECB encryption mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes pt: already padded plaintext + """ + if not pt or len(pt) % bs != 0: + raise ValueError("Plaintext is not blocksize aligned") + ct = [] + for i in xrange(0, len(pt), bs): + ct.append(encrypter(pt[i:i + bs])) + return b"".join(ct) + + +def ecb_decrypt(decrypter, bs, ct): + """ECB decryption mode of operation + + :param decrypter: Decrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes ct: ciphertext + """ + if not ct or len(ct) % bs != 0: + raise ValueError("Ciphertext is not blocksize aligned") + pt = [] + for i in xrange(0, len(ct), bs): + pt.append(decrypter(ct[i:i + bs])) + return b"".join(pt) + + +def acpkm(encrypter, bs): + """Perform ACPKM key derivation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + """ + return b"".join([ + encrypter(bytes(bytearray(range(d, d + bs)))) + for d in range(0x80, 0x80 + bs * (KEYSIZE // bs), bs) + ]) + + +def ctr(encrypter, bs, data, iv, _acpkm=None): + """Counter mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes data: plaintext/ciphertext + :param bytes iv: half blocksize-sized initialization vector + + For decryption you use the same function again. + """ + if len(iv) != bs // 2: + raise ValueError("Invalid IV size") + if len(data) > bs * (1 << (8 * (bs // 2 - 1))): + raise ValueError("Too big data") + stream = [] + ctr_value = 0 + ctr_max_value = 1 << (8 * (bs // 2)) + if _acpkm is not None: + acpkm_algo_class, acpkm_section_size_in_bs = _acpkm + acpkm_section_size_in_bs //= bs + for _ in xrange(0, len(data) + pad_size(len(data), bs), bs): + if ( + _acpkm is not None and + ctr_value != 0 and + ctr_value % acpkm_section_size_in_bs == 0 + ): + encrypter = acpkm_algo_class(acpkm(encrypter, bs)).encrypt + stream.append(encrypter(iv + long2bytes(ctr_value, bs // 2))) + ctr_value = (ctr_value + 1) % ctr_max_value + return strxor(b"".join(stream), data) + + +def ctr_acpkm(algo_class, encrypter, section_size, bs, data, iv): + """CTR-ACPKM mode of operation + + :param algo_class: pygost.gost3412's algorithm class + :param encrypter: encrypting function, that takes block as an input + :param int section_size: ACPKM'es section size (N), in bytes + :param int bs: cipher's blocksize, bytes + :param bytes data: plaintext/ciphertext + :param bytes iv: half blocksize-sized initialization vector + + For decryption you use the same function again. + """ + if section_size % bs != 0: + raise ValueError("section_size must be multiple of bs") + return ctr(encrypter, bs, data, iv, _acpkm=(algo_class, section_size)) + + +def ofb(encrypter, bs, data, iv): + """OFB mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes data: plaintext/ciphertext + :param bytes iv: blocksize-sized initialization vector + + For decryption you use the same function again. + """ + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + result = [] + for i in xrange(0, len(data) + pad_size(len(data), bs), bs): + r = r[1:] + [encrypter(r[0])] + result.append(strxor(r[-1], data[i:i + bs])) + return b"".join(result) + + +def cbc_encrypt(encrypter, bs, pt, iv): + """CBC encryption mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes pt: already padded plaintext + :param bytes iv: blocksize-sized initialization vector + """ + if not pt or len(pt) % bs != 0: + raise ValueError("Plaintext is not blocksize aligned") + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + ct = [] + for i in xrange(0, len(pt), bs): + ct.append(encrypter(strxor(r[0], pt[i:i + bs]))) + r = r[1:] + [ct[-1]] + return b"".join(ct) + + +def cbc_decrypt(decrypter, bs, ct, iv): + """CBC decryption mode of operation + + :param decrypter: Decrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes ct: ciphertext + :param bytes iv: blocksize-sized initialization vector + """ + if not ct or len(ct) % bs != 0: + raise ValueError("Ciphertext is not blocksize aligned") + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + pt = [] + for i in xrange(0, len(ct), bs): + blk = ct[i:i + bs] + pt.append(strxor(r[0], decrypter(blk))) + r = r[1:] + [blk] + return b"".join(pt) + + +def cfb_encrypt(encrypter, bs, pt, iv): + """CFB encryption mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes pt: plaintext + :param bytes iv: blocksize-sized initialization vector + """ + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + ct = [] + for i in xrange(0, len(pt) + pad_size(len(pt), bs), bs): + ct.append(strxor(encrypter(r[0]), pt[i:i + bs])) + r = r[1:] + [ct[-1]] + return b"".join(ct) + + +def cfb_decrypt(encrypter, bs, ct, iv): + """CFB decryption mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes ct: ciphertext + :param bytes iv: blocksize-sized initialization vector + """ + if len(iv) < bs or len(iv) % bs != 0: + raise ValueError("Invalid IV size") + r = [iv[i:i + bs] for i in range(0, len(iv), bs)] + pt = [] + for i in xrange(0, len(ct) + pad_size(len(ct), bs), bs): + blk = ct[i:i + bs] + pt.append(strxor(encrypter(r[0]), blk)) + r = r[1:] + [blk] + return b"".join(pt) + + +def _mac_shift(bs, data, xor_lsb=0): + num = (bytes2long(data) << 1) ^ xor_lsb + return long2bytes(num, bs)[-bs:] + + +Rb64 = 0b11011 +Rb128 = 0b10000111 + + +def _mac_ks(encrypter, bs): + Rb = Rb128 if bs == 16 else Rb64 + _l = encrypter(bs * b"\x00") + k1 = _mac_shift(bs, _l, Rb) if bytearray(_l)[0] & 0x80 > 0 else _mac_shift(bs, _l) + k2 = _mac_shift(bs, k1, Rb) if bytearray(k1)[0] & 0x80 > 0 else _mac_shift(bs, k1) + return k1, k2 + + +def mac(encrypter, bs, data): + """MAC (known here as CMAC, OMAC1) mode of operation + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize, bytes + :param bytes data: data to authenticate + + Implementation is based on PyCrypto's CMAC one, that is in public domain. + """ + k1, k2 = _mac_ks(encrypter, bs) + if len(data) % bs == 0: + tail_offset = len(data) - bs + else: + tail_offset = len(data) - (len(data) % bs) + prev = bs * b"\x00" + for i in xrange(0, tail_offset, bs): + prev = encrypter(strxor(data[i:i + bs], prev)) + tail = data[tail_offset:] + return encrypter(strxor( + strxor(pad3(tail, bs), prev), + k1 if len(tail) == bs else k2, + )) + + +def acpkm_master(algo_class, encrypter, key_section_size, bs, keymat_len): + """ACPKM-Master key derivation + + :param algo_class: pygost.gost3412's algorithm class + :param encrypter: encrypting function, that takes block as an input + :param int key_section_size: ACPKM'es key section size (T*), in bytes + :param int bs: cipher's blocksize, bytes + :param int keymat_len: length of key material to produce + """ + return ctr_acpkm( + algo_class, + encrypter, + key_section_size, + bs, + data=b"\x00" * keymat_len, + iv=b"\xFF" * (bs // 2), + ) + + +def mac_acpkm_master(algo_class, encrypter, key_section_size, section_size, bs, data): + """OMAC-ACPKM-Master + + :param algo_class: pygost.gost3412's algorithm class + :param encrypter: encrypting function, that takes block as an input + :param int key_section_size: ACPKM'es key section size (T*), in bytes + :param int section_size: ACPKM'es section size (N), in bytes + :param int bs: cipher's blocksize, bytes + :param bytes data: data to authenticate + """ + if len(data) % bs == 0: + tail_offset = len(data) - bs + else: + tail_offset = len(data) - (len(data) % bs) + prev = bs * b"\x00" + sections = len(data) // section_size + if len(data) % section_size != 0: + sections += 1 + keymats = acpkm_master( + algo_class, + encrypter, + key_section_size, + bs, + (KEYSIZE + bs) * sections, + ) + for i in xrange(0, tail_offset, bs): + if i % section_size == 0: + keymat, keymats = keymats[:KEYSIZE + bs], keymats[KEYSIZE + bs:] + key, k1 = keymat[:KEYSIZE], keymat[KEYSIZE:] + encrypter = algo_class(key).encrypt + prev = encrypter(strxor(data[i:i + bs], prev)) + tail = data[tail_offset:] + if len(tail) == bs: + key, k1 = keymats[:KEYSIZE], keymats[KEYSIZE:] + encrypter = algo_class(key).encrypt + k2 = long2bytes(bytes2long(k1) << 1, size=bs) + if bytearray(k1)[0] & 0x80 != 0: + k2 = strxor(k2, long2bytes(Rb128 if bs == 16 else Rb64, size=bs)) + return encrypter(strxor( + strxor(pad3(tail, bs), prev), + k1 if len(tail) == bs else k2, + )) + + +def pad_iso10126(data, blocksize): + """ISO 10126 padding + + Does not exist in 34.13, but added for convenience. + It uses urandom call for getting the randomness. + """ + pad_len = blocksize - len(data) % blocksize + if pad_len == 0: + pad_len = blocksize + return b"".join((data, urandom(pad_len - 1), bytes((pad_len,)))) + + +def unpad_iso10126(data, blocksize): + """Unpad :py:func:`pygost.gost3413.pad_iso10126` + """ + if len(data) % blocksize != 0: + raise ValueError("Data length is not multiple of blocksize") + pad_len = bytearray(data)[-1] + if pad_len > blocksize: + raise ValueError("Padding length is bigger than blocksize") + return data[:-pad_len] diff --git a/pygost-5.13/pygost/iface.py b/pygost-5.13/pygost/iface.py new file mode 100644 index 0000000..e2d6a4c --- /dev/null +++ b/pygost-5.13/pygost/iface.py @@ -0,0 +1,50 @@ +from abc import ABCMeta +from abc import abstractmethod + +from pygost.utils import hexenc + + +# This function is taken from six package as is +def add_metaclass(metaclass): + """Class decorator for creating a class with a metaclass.""" + def wrapper(cls): + orig_vars = cls.__dict__.copy() + slots = orig_vars.get("__slots__") + if slots is not None: + if isinstance(slots, str): + slots = [slots] + for slots_var in slots: + orig_vars.pop(slots_var) + orig_vars.pop("__dict__", None) + orig_vars.pop("__weakref__", None) + return metaclass(cls.__name__, cls.__bases__, orig_vars) + return wrapper + + +@add_metaclass(ABCMeta) +class PEP247(object): + @property + @abstractmethod + def digest_size(self): + """The size of the digest produced by the hashing objects. + """ + + @abstractmethod + def copy(self): + """Return a separate copy of this hashing object. + """ + + @abstractmethod + def update(self, data): + """Hash data into the current state of the hashing object. + """ + + @abstractmethod + def digest(self): + """Return the hash value as a string containing 8-bit data. + """ + + def hexdigest(self): + """Return the hash value as a string containing hexadecimal digits. + """ + return hexenc(self.digest()) diff --git a/pygost-5.13/pygost/kdf.py b/pygost-5.13/pygost/kdf.py new file mode 100644 index 0000000..4e404c6 --- /dev/null +++ b/pygost-5.13/pygost/kdf.py @@ -0,0 +1,81 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Key derivation functions, Р 50.1.113-2016, Р 1323565.1.020-2018 +""" + +import hmac + +from pygost.gost3410_vko import kek_34102012256 +from pygost.gost3410_vko import kek_34102012512 +from pygost.gost34112012256 import GOST34112012256 +from pygost.utils import bytes2long +from pygost.utils import long2bytes + + +def kdf_gostr3411_2012_256(key, label, seed): + """KDF_GOSTR3411_2012_256 + + :param bytes key: initial key + :param bytes label: label + :param bytes seed: seed + :returns: 32 bytes + """ + return hmac.new( + key=key, + msg=b"".join((b"\x01", label, b"\x00", seed, b"\x01\x00")), + digestmod=GOST34112012256, + ).digest() + + +def kdf_tree_gostr3411_2012_256(key, label, seed, keys, i_len=1): + """KDF_TREE_GOSTR3411_2012_256 + + :param bytes key: initial key + :param bytes label: label + :param bytes seed: seed + :param int keys: number of generated keys + :param int i_len: length of iterations value (called "R") + :returns: list of 256-bit keys + """ + keymat = [] + _len = long2bytes(keys * 32 * 8, size=1) + for i in range(keys): + keymat.append(hmac.new( + key=key, + msg=b"".join((long2bytes(i + 1, size=i_len), label, b"\x00", seed, _len)), + digestmod=GOST34112012256, + ).digest()) + return keymat + + +def keg(curve, prv, pub, h): + """Export key generation (Р 1323565.1.020-2018) + + :param GOST3410Curve curve: curve to use + :param long prv: private key + :param pub: public key + :type pub: (long, long) + :param bytes h: "h"-value, 32 bytes + """ + if len(h) != 32: + raise ValueError("h must be 32 bytes long") + ukm = bytes2long(h[:16]) + if ukm == 0: + ukm = 1 + if curve.point_size == 64: + return kek_34102012512(curve, prv, pub, ukm) + k_exp = kek_34102012256(curve, prv, pub, ukm) + return b"".join(kdf_tree_gostr3411_2012_256(k_exp, b"kdf tree", h[16:24], 2)) diff --git a/pygost-5.13/pygost/mgm.py b/pygost-5.13/pygost/mgm.py new file mode 100644 index 0000000..fb51343 --- /dev/null +++ b/pygost-5.13/pygost/mgm.py @@ -0,0 +1,168 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Multilinear Galois Mode (MGM) block cipher mode. +""" + +from hmac import compare_digest + +from pygost.gost3413 import pad1 +from pygost.utils import bytes2long +from pygost.utils import long2bytes +from pygost.utils import strxor + + +def _incr(data, bs): + return long2bytes(bytes2long(data) + 1, size=bs // 2) + + +def incr_r(data, bs): + return data[:bs // 2] + _incr(data[bs // 2:], bs) + + +def incr_l(data, bs): + return _incr(data[:bs // 2], bs) + data[bs // 2:] + + +def nonce_prepare(nonce): + """Prepare nonce for MGM usage + + It just clears MSB. + """ + n = bytearray(nonce) + n[0] &= 0x7F + return bytes(n) + + +class MGM(object): + # Implementation is fully based on go.cypherpunks.ru/gogost/mgm + def __init__(self, encrypter, bs, tag_size=None): + """Multilinear Galois Mode (MGM) block cipher mode + + :param encrypter: encrypting function, that takes block as an input + :param int bs: cipher's blocksize + :param int tag_size: authentication tag size + (defaults to blocksize if not specified) + """ + if bs not in (8, 16): + raise ValueError("Only 64/128-bit blocksizes allowed") + self.tag_size = bs if tag_size is None else tag_size + if self.tag_size < 4 or self.tag_size > bs: + raise ValueError("Invalid tag_size") + self.encrypter = encrypter + self.bs = bs + self.max_size = (1 << (bs * 8 // 2)) - 1 + self.r = 0x1B if bs == 8 else 0x87 + + def _validate_nonce(self, nonce): + if len(nonce) != self.bs: + raise ValueError("nonce length must be equal to cipher's blocksize") + if bytearray(nonce)[0] & 0x80 > 0: + raise ValueError("nonce must not have higher bit set") + + def _validate_sizes(self, plaintext, additional_data): + if len(plaintext) == 0 and len(additional_data) == 0: + raise ValueError("At least one of plaintext or additional_data required") + if len(plaintext) + len(additional_data) > self.max_size: + raise ValueError("plaintext+additional_data are too big") + + def _mul(self, x, y): + x = bytes2long(x) + y = bytes2long(y) + z = 0 + max_bit = 1 << (self.bs * 8 - 1) + while y > 0: + if y & 1 == 1: + z ^= x + if x & max_bit > 0: + x = ((x ^ max_bit) << 1) ^ self.r + else: + x <<= 1 + y >>= 1 + return long2bytes(z, size=self.bs) + + def _crypt(self, icn, data): + icn[0] &= 0x7F + enc = self.encrypter(bytes(icn)) + res = [] + while len(data) > 0: + res.append(strxor(self.encrypter(enc), data)) + enc = incr_r(enc, self.bs) + data = data[self.bs:] + return b"".join(res) + + def _auth(self, icn, text, ad): + icn[0] |= 0x80 + enc = self.encrypter(bytes(icn)) + _sum = self.bs * b"\x00" + ad_len = len(ad) + text_len = len(text) + while len(ad) > 0: + _sum = strxor(_sum, self._mul( + self.encrypter(enc), + pad1(ad[:self.bs], self.bs), + )) + enc = incr_l(enc, self.bs) + ad = ad[self.bs:] + while len(text) > 0: + _sum = strxor(_sum, self._mul( + self.encrypter(enc), + pad1(text[:self.bs], self.bs), + )) + enc = incr_l(enc, self.bs) + text = text[self.bs:] + _sum = strxor(_sum, self._mul(self.encrypter(enc), ( + long2bytes(ad_len * 8, size=self.bs // 2) + + long2bytes(text_len * 8, size=self.bs // 2) + ))) + return self.encrypter(_sum)[:self.tag_size] + + def seal(self, nonce, plaintext, additional_data): + """Seal plaintext + + :param bytes nonce: blocksize-sized nonce. + Assure that it does not have MSB bit set + (:py:func:`pygost.mgm.nonce_prepare` helps) + :param bytes plaintext: plaintext to be encrypted and authenticated + :param bytes additional_data: additional data to be authenticated + """ + self._validate_nonce(nonce) + self._validate_sizes(plaintext, additional_data) + icn = bytearray(nonce) + ciphertext = self._crypt(icn, plaintext) + tag = self._auth(icn, ciphertext, additional_data) + return ciphertext + tag + + def open(self, nonce, ciphertext, additional_data): + """Open ciphertext + + :param bytes nonce: blocksize-sized nonce. + Assure that it does not have MSB bit set + (:py:func:`pygost.mgm.nonce_prepare` helps) + :param bytes ciphertext: ciphertext to be decrypted and authenticated + :param bytes additional_data: additional data to be authenticated + :raises ValueError: if ciphertext authentication fails + """ + self._validate_nonce(nonce) + self._validate_sizes(ciphertext, additional_data) + icn = bytearray(nonce) + ciphertext, tag_expected = ( + ciphertext[:-self.tag_size], + ciphertext[-self.tag_size:], + ) + tag = self._auth(icn, ciphertext, additional_data) + if not compare_digest(tag_expected, tag): + raise ValueError("Invalid authentication tag") + return self._crypt(icn, ciphertext) diff --git a/pygost-5.13/pygost/pbkdf2.py b/pygost-5.13/pygost/pbkdf2.py new file mode 100644 index 0000000..0fd6ddc --- /dev/null +++ b/pygost-5.13/pygost/pbkdf2.py @@ -0,0 +1,41 @@ +# coding: utf-8 +"""PBKDF2 implementation suitable for GOST R 34.11-94/34.11-2012. + +This implementation is based on Python 3.5.2 source code's one. +PyGOST does not register itself in hashlib anyway, so use it instead. +""" + + +from pygost.utils import bytes2long +from pygost.utils import long2bytes +from pygost.utils import strxor +from pygost.utils import xrange + + +def pbkdf2(hasher, password, salt, iterations, dklen): + """PBKDF2 implementation suitable for GOST R 34.11-94/34.11-2012 + """ + inner = hasher() + outer = hasher() + password = password + b"\x00" * (inner.block_size - len(password)) + inner.update(strxor(password, len(password) * b"\x36")) + outer.update(strxor(password, len(password) * b"\x5C")) + + def prf(msg): + icpy = inner.copy() + ocpy = outer.copy() + icpy.update(msg) + ocpy.update(icpy.digest()) + return ocpy.digest() + + dkey = b"" + loop = 1 + while len(dkey) < dklen: + prev = prf(salt + long2bytes(loop, 4)) + rkey = bytes2long(prev) + for _ in xrange(iterations - 1): + prev = prf(prev) + rkey ^= bytes2long(prev) + loop += 1 + dkey += long2bytes(rkey, inner.digest_size) + return dkey[:dklen] diff --git a/pygost-5.13/pygost/stubs/pygost/__init__.pyi b/pygost-5.13/pygost/stubs/pygost/__init__.pyi new file mode 100644 index 0000000..e69de29 diff --git a/pygost-5.13/pygost/stubs/pygost/gost28147.pyi b/pygost-5.13/pygost/stubs/pygost/gost28147.pyi new file mode 100644 index 0000000..be31261 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost28147.pyi @@ -0,0 +1,101 @@ +from typing import Callable +from typing import Dict +from typing import Sequence +from typing import Tuple + + +SBOXES = ... # type: Dict[str, Tuple[Tuple[int, ...], ...]] +BLOCKSIZE = ... # type: int + +Words = Tuple[int, int] + + +def block2ns(data: bytes) -> Words: ... + + +def ns2block(ns: Words) -> bytes: ... + + +def validate_key(key: bytes) -> None: ... + + +def validate_iv(iv: bytes) -> None: ... + + +def validate_sbox(sbox: str) -> None: ... + + +def xcrypt(seq: Sequence[int], sbox: str, key: bytes, ns: Words) -> Words: ... + + +def encrypt(sbox: str, key: bytes, ns: Words) -> Words: ... + + +def decrypt(sbox: str, key: bytes, ns: Words) -> Words: ... + + +def ecb( + key: bytes, + data: bytes, + action: Callable[[str, bytes, Words], Words], + sbox: str = ..., +) -> bytes: ... + + +def ecb_encrypt( + key: bytes, + data: bytes, + sbox: str = ..., +) -> bytes: ... + + +def ecb_decrypt( + key: bytes, + data: bytes, + sbox: str = ..., +) -> bytes: ... + + +def cbc_encrypt( + key: bytes, + data: bytes, + iv: bytes = ..., + pad: bool = ..., + sbox: str = ..., + mesh: bool = ..., +) -> bytes: ... + + +def cbc_decrypt( + key: bytes, + data: bytes, + pad: bool = ..., + sbox: str = ..., + mesh: bool = ..., +) -> bytes: ... + + +def cnt( + key: bytes, + data: bytes, + iv: bytes = ..., + sbox: str = ..., +) -> bytes: ... + + +def cfb_encrypt( + key: bytes, + data: bytes, + iv: bytes = ..., + sbox: str = ..., + mesh: bool = ..., +) -> bytes: ... + + +def cfb_decrypt( + key: bytes, + data: bytes, + iv: bytes = ..., + sbox: str = ..., + mesh: bool = ..., +) -> bytes: ... diff --git a/pygost-5.13/pygost/stubs/pygost/gost28147_mac.pyi b/pygost-5.13/pygost/stubs/pygost/gost28147_mac.pyi new file mode 100644 index 0000000..70d90d6 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost28147_mac.pyi @@ -0,0 +1,25 @@ +from pygost.iface import PEP247 + + +class MAC(PEP247): + def __init__( + self, + key: bytes, + data: bytes = ..., + iv: bytes = ..., + sbox: str = ..., + ) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "MAC": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... + + +def new(key: bytes, data: bytes = ..., iv: bytes = ..., sbox: str = ...) -> MAC: ... diff --git a/pygost-5.13/pygost/stubs/pygost/gost3410.pyi b/pygost-5.13/pygost/stubs/pygost/gost3410.pyi new file mode 100644 index 0000000..8f0dcb8 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost3410.pyi @@ -0,0 +1,72 @@ +from typing import Dict +from typing import Tuple + + +DEFAULT_CURVE = ... # type: GOST3410Curve +CURVES = ... # type: Dict[str, GOST3410Curve] +PublicKey = Tuple[int, int] + + +class GOST3410Curve(object): + p = ... # type: int + q = ... # type: int + a = ... # type: int + b = ... # type: int + x = ... # type: int + y = ... # type: int + cofactor = ... # type: int + e = ... # type: int + d = ... # type: int + name = ... # type: str + + def __init__( + self, + p: int, + q: int, + a: int, + b: int, + x: int, + y: int, + cofactor: int = 1, + e: int = None, + d: int = None, + name: str = None, + ) -> None: ... + + def pos(self, v: int) -> int: ... + + def exp(self, degree: int, x: int = ..., y: int = ...) -> int: ... + + def st(self) -> Tuple[int, int]: ... + + @property + def point_size(self) -> int: ... + + def contains(self, point: Tuple[int, int]) -> bool: ... + + +def public_key(curve: GOST3410Curve, prv: int) -> PublicKey: ... + + +def sign(curve: GOST3410Curve, prv: int, digest: bytes, rand: bytes = None) -> bytes: ... + + +def verify(curve: GOST3410Curve, pub: PublicKey, digest: bytes, signature: bytes) -> bool: ... + + +def prv_unmarshal(prv: bytes) -> int: ... + + +def prv_marshal(curve: GOST3410Curve, prv: int) -> bytes: ... + + +def pub_marshal(pub: PublicKey) -> bytes: ... + + +def pub_unmarshal(pub: bytes) -> PublicKey: ... + + +def uv2xy(curve: GOST3410Curve, u: int, v: int) -> Tuple[int, int]: ... + + +def xy2uv(curve: GOST3410Curve, x: int, y: int) -> Tuple[int, int]: ... diff --git a/pygost-5.13/pygost/stubs/pygost/gost3410_vko.pyi b/pygost-5.13/pygost/stubs/pygost/gost3410_vko.pyi new file mode 100644 index 0000000..6ea9b82 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost3410_vko.pyi @@ -0,0 +1,17 @@ +from pygost.gost3410 import GOST3410Curve +from pygost.gost3410 import PublicKey + + +def ukm_unmarshal(ukm: bytes) -> int: ... + + +def kek(curve: GOST3410Curve, prv: int, pub: PublicKey, ukm: int) -> bytes: ... + + +def kek_34102001(curve: GOST3410Curve, prv: int, pub: PublicKey, ukm: int) -> bytes: ... + + +def kek_34102012256(curve: GOST3410Curve, prv: int, pub: PublicKey, ukm: int = ...) -> bytes: ... + + +def kek_34102012512(curve: GOST3410Curve, prv: int, pub: PublicKey, ukm: int = ...) -> bytes: ... diff --git a/pygost-5.13/pygost/stubs/pygost/gost34112012.pyi b/pygost-5.13/pygost/stubs/pygost/gost34112012.pyi new file mode 100644 index 0000000..3d5cc41 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost34112012.pyi @@ -0,0 +1,18 @@ +from pygost.iface import PEP247 + + +class GOST34112012(PEP247): + block_size = ... # type: int + + def __init__(self, data: bytes = ..., digest_size: int = ...) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "GOST34112012": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... diff --git a/pygost-5.13/pygost/stubs/pygost/gost34112012256.pyi b/pygost-5.13/pygost/stubs/pygost/gost34112012256.pyi new file mode 100644 index 0000000..a1d2a01 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost34112012256.pyi @@ -0,0 +1,21 @@ +from pygost.iface import PEP247 + + +class GOST34112012256(PEP247): + block_size = ... # type: int + + def __init__(self, data: bytes = ...) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "GOST34112012256": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... + + +def new(data: bytes = ...) -> GOST34112012256: ... diff --git a/pygost-5.13/pygost/stubs/pygost/gost34112012512.pyi b/pygost-5.13/pygost/stubs/pygost/gost34112012512.pyi new file mode 100644 index 0000000..349bddd --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost34112012512.pyi @@ -0,0 +1,24 @@ +from pygost.iface import PEP247 + + +class GOST34112012512(PEP247): + block_size = ... # type: int + + def __init__(self, data: bytes = ...) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "GOST34112012512": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... + + +def new(data: bytes = ...) -> GOST34112012512: ... + + +def pbkdf2(password: bytes, salt: bytes, iterations: int, dklen: int) -> bytes: ... diff --git a/pygost-5.13/pygost/stubs/pygost/gost341194.pyi b/pygost-5.13/pygost/stubs/pygost/gost341194.pyi new file mode 100644 index 0000000..24de2e4 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost341194.pyi @@ -0,0 +1,25 @@ +from pygost.iface import PEP247 + + +class GOST341194(PEP247): + sbox = ... # type: str + block_size = ... # type: int + + def __init__(self, data: bytes = ..., sbox: str = ...) -> None: ... + + @property + def digest_size(self) -> int: ... + + def copy(self) -> "GOST341194": ... + + def update(self, data: bytes) -> None: ... + + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... + + +def new(data: bytes = ..., sbox: str = ...) -> GOST341194: ... + + +def pbkdf2(password: bytes, salt: bytes, iterations: int, dklen: int) -> bytes: ... diff --git a/pygost-5.13/pygost/stubs/pygost/gost3412.pyi b/pygost-5.13/pygost/stubs/pygost/gost3412.pyi new file mode 100644 index 0000000..ef278b7 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost3412.pyi @@ -0,0 +1,18 @@ +class GOST3412Kuznechik(object): + blocksize = ... # type: int + + def __init__(self, key: bytes) -> None: ... + + def encrypt(self, blk: bytes) -> bytes: ... + + def decrypt(self, blk: bytes) -> bytes: ... + + +class GOST3412Magma(object): + blocksize = ... # type: int + + def __init__(self, key: bytes) -> None: ... + + def encrypt(self, blk: bytes) -> bytes: ... + + def decrypt(self, blk: bytes) -> bytes: ... diff --git a/pygost-5.13/pygost/stubs/pygost/gost3413.pyi b/pygost-5.13/pygost/stubs/pygost/gost3413.pyi new file mode 100644 index 0000000..4cfd694 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/gost3413.pyi @@ -0,0 +1,81 @@ +from typing import Callable + + +def pad_size(data_size: int, blocksize: int) -> int: ... + + +def pad1(data: bytes, blocksize: int) -> bytes: ... + + +def pad2(data: bytes, blocksize: int) -> bytes: ... + + +def unpad2(data: bytes, blocksize: int) -> bytes: ... + + +def pad3(data: bytes, blocksize: int) -> bytes: ... + + +def ecb_encrypt(encrypter: Callable[[bytes], bytes], bs: int, pt: bytes) -> bytes: ... + + +def ecb_decrypt(decrypter: Callable[[bytes], bytes], bs: int, ct: bytes) -> bytes: ... + + +def acpkm(encrypter: Callable[[bytes], bytes], bs: int) -> bytes: ... + + +def ctr(encrypter: Callable[[bytes], bytes], bs: int, data: bytes, iv: bytes) -> bytes: ... + + +def ctr_acpkm( + algo_class: object, + encrypter: Callable[[bytes], bytes], + section_size: int, + bs: int, + data: bytes, + iv: bytes, +) -> bytes: ... + + +def ofb(encrypter: Callable[[bytes], bytes], bs: int, data: bytes, iv: bytes) -> bytes: ... + + +def cbc_encrypt(encrypter: Callable[[bytes], bytes], bs: int, pt: bytes, iv: bytes) -> bytes: ... + + +def cbc_decrypt(decrypter: Callable[[bytes], bytes], bs: int, ct: bytes, iv: bytes) -> bytes: ... + + +def cfb_encrypt(encrypter: Callable[[bytes], bytes], bs: int, pt: bytes, iv: bytes) -> bytes: ... + + +def cfb_decrypt(encrypter: Callable[[bytes], bytes], bs: int, ct: bytes, iv: bytes) -> bytes: ... + + +def mac(encrypter: Callable[[bytes], bytes], bs: int, data: bytes) -> bytes: ... + + +def acpkm_master( + algo_class: object, + encrypter: Callable[[bytes], bytes], + key_section_size: int, + bs: int, + keymat_len: int, +) -> bytes: ... + + +def mac_acpkm_master( + algo_class: object, + encrypter: Callable[[bytes], bytes], + key_section_size: int, + section_size: int, + bs: int, + data: bytes, +) -> bytes: ... + + +def pad_iso10126(data: bytes, blocksize: int) -> bytes: ... + + +def unpad_iso10126(data: bytes, blocksize: int) -> bytes: ... diff --git a/pygost-5.13/pygost/stubs/pygost/iface.pyi b/pygost-5.13/pygost/stubs/pygost/iface.pyi new file mode 100644 index 0000000..a5c2a85 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/iface.pyi @@ -0,0 +1,19 @@ +from abc import ABCMeta +from abc import abstractmethod + + +class PEP247(metaclass=ABCMeta): + @abstractmethod + @property + def digest_size(self) -> int: ... + + @abstractmethod + def copy(self) -> "PEP247": ... + + @abstractmethod + def update(self, data: bytes) -> None: ... + + @abstractmethod + def digest(self) -> bytes: ... + + def hexdigest(self) -> str: ... diff --git a/pygost-5.13/pygost/stubs/pygost/kdf.pyi b/pygost-5.13/pygost/stubs/pygost/kdf.pyi new file mode 100644 index 0000000..ccab8af --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/kdf.pyi @@ -0,0 +1,22 @@ +from typing import Sequence +from typing import Tuple + +from pygost.gost3410 import GOST3410Curve + + +PublicKey = Tuple[int, int] + + +def kdf_gostr3411_2012_256(key: bytes, label: bytes, seed: bytes) -> bytes: ... + + +def kdf_tree_gostr3411_2012_256( + key: bytes, + label: bytes, + seed: bytes, + keys: int, + i_len: int = 1, +) -> Sequence[bytes]: ... + + +def keg(curve: GOST3410Curve, prv: int, pub: PublicKey, h: bytes) -> bytes: ... diff --git a/pygost-5.13/pygost/stubs/pygost/mgm.pyi b/pygost-5.13/pygost/stubs/pygost/mgm.pyi new file mode 100644 index 0000000..81906b7 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/mgm.pyi @@ -0,0 +1,17 @@ +from typing import Callable + + +def nonce_prepare(nonce: bytes) -> bytes: ... + + +class MGM(object): + def __init__( + self, + encrypter: Callable[[bytes], bytes], + bs: int, + tag_size: int = None, + ) -> None: ... + + def seal(self, nonce: bytes, plaintext: bytes, additional_data: bytes) -> bytes: ... + + def open(self, nonce: bytes, ciphertext: bytes, additional_data: bytes) -> bytes: ... diff --git a/pygost-5.13/pygost/stubs/pygost/utils.pyi b/pygost-5.13/pygost/stubs/pygost/utils.pyi new file mode 100644 index 0000000..76460e5 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/utils.pyi @@ -0,0 +1,19 @@ +from typing import AnyStr + + +def strxor(a: bytes, b: bytes) -> bytes: ... + + +def hexdec(data: AnyStr) -> bytes: ... + + +def hexenc(data: bytes) -> str: ... + + +def bytes2long(raw: bytes) -> int: ... + + +def long2bytes(n: int, size: int = ...) -> bytes: ... + + +def modinvert(a: int, n: int) -> int: ... diff --git a/pygost-5.13/pygost/stubs/pygost/wrap.pyi b/pygost-5.13/pygost/stubs/pygost/wrap.pyi new file mode 100644 index 0000000..776a6e7 --- /dev/null +++ b/pygost-5.13/pygost/stubs/pygost/wrap.pyi @@ -0,0 +1,31 @@ +from typing import Callable + + +def wrap_gost(ukm: bytes, kek: bytes, cek: bytes, sbox: str = ...) -> bytes: ... + + +def unwrap_gost(kek: bytes, data: bytes, sbox: str = ...) -> bytes: ... + + +def wrap_cryptopro(ukm: bytes, kek: bytes, cek: bytes, sbox: str = ...) -> bytes: ... + + +def unwrap_cryptopro(kek: bytes, data: bytes, sbox: str = ...) -> bytes: ... + + +def kexp15( + encrypter_key: Callable[[bytes], bytes], + encrypter_mac: Callable[[bytes], bytes], + bs: int, + key: bytes, + iv: bytes, +) -> bytes: ... + + +def kimp15( + encrypter_key: Callable[[bytes], bytes], + encrypter_mac: Callable[[bytes], bytes], + bs: int, + kexp: bytes, + iv: bytes, +) -> bytes: ... diff --git a/pygost-5.13/pygost/test_cms.py b/pygost-5.13/pygost/test_cms.py new file mode 100644 index 0000000..7e5781a --- /dev/null +++ b/pygost-5.13/pygost/test_cms.py @@ -0,0 +1,1078 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from base64 import b64decode +from unittest import skipIf +from unittest import TestCase + +from six import text_type + +from pygost.gost28147 import cfb_decrypt +from pygost.gost3410 import CURVES +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import pub_marshal +from pygost.gost3410 import pub_unmarshal +from pygost.gost3410 import public_key +from pygost.gost3410 import verify +from pygost.gost3410_vko import kek_34102012256 +from pygost.gost3410_vko import ukm_unmarshal +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3413 import ctr_acpkm +from pygost.gost3413 import KEYSIZE +from pygost.gost3413 import mac as omac +from pygost.kdf import kdf_tree_gostr3411_2012_256 +from pygost.kdf import keg +from pygost.utils import hexdec +from pygost.wrap import kimp15 +from pygost.wrap import unwrap_cryptopro +from pygost.wrap import unwrap_gost + +try: + from pyderasn import DecodePathDefBy + from pyderasn import OctetString + + from pygost.asn1schemas.cms import ContentInfo + from pygost.asn1schemas.cms import SignedAttributes + from pygost.asn1schemas.oids import id_cms_mac_attr + from pygost.asn1schemas.oids import id_envelopedData + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm_omac + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_wrap_kexp15 + from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm + from pygost.asn1schemas.oids import id_gostr3412_2015_magma_ctracpkm_omac + from pygost.asn1schemas.oids import id_gostr3412_2015_magma_wrap_kexp15 + from pygost.asn1schemas.oids import id_messageDigest + from pygost.asn1schemas.oids import id_tc26_agreement_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_agreement_gost3410_2012_512 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetA + from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256 + from pygost.asn1schemas.oids import id_tc26_gost3411_2012_512 + from pygost.asn1schemas.x509 import Certificate + from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters +except ImportError: + pyderasn_exists = False +else: + pyderasn_exists = True + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestSigned(TestCase): + """SignedData test vectors from "Использование + алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 в + криптографических сообщениях формата CMS" (TK26CMS.pdf) + """ + + def process_cms( + self, + content_info_raw, + prv_key_raw, + curve_name, + hasher, + ): + content_info, tail = ContentInfo().decode(content_info_raw) + self.assertSequenceEqual(tail, b"") + self.assertIsNotNone(content_info["content"].defined) + _, signed_data = content_info["content"].defined + self.assertEqual(len(signed_data["signerInfos"]), 1) + curve = CURVES[curve_name] + self.assertTrue(verify( + curve, + public_key(curve, prv_unmarshal(prv_key_raw)), + hasher(bytes(signed_data["encapContentInfo"]["eContent"])).digest()[::-1], + bytes(signed_data["signerInfos"][0]["signature"]), + )) + + def test_256(self): + content_info_raw = b64decode(""" +MIIBBQYJKoZIhvcNAQcCoIH3MIH0AgEBMQ4wDAYIKoUDBwEBAgIFADAbBgkqhkiG +9w0BBwGgDgQMVGVzdCBtZXNzYWdlMYHBMIG+AgEBMFswVjEpMCcGCSqGSIb3DQEJ +ARYaR29zdFIzNDEwLTIwMTJAZXhhbXBsZS5jb20xKTAnBgNVBAMTIEdvc3RSMzQx +MC0yMDEyICgyNTYgYml0KSBleGFtcGxlAgEBMAwGCCqFAwcBAQICBQAwDAYIKoUD +BwEBAQEFAARAkptb2ekZbC94FaGDQeP70ExvTkXtOY9zgz3cCco/hxPhXUVo3eCx +VNwDQ8enFItJZ8DEX4blZ8QtziNCMl5HbA== + """) + prv_key_raw = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1] + self.process_cms( + content_info_raw, + prv_key_raw, + "id-GostR3410-2001-CryptoPro-XchA-ParamSet", + GOST34112012256, + ) + + def test_512(self): + content_info_raw = b64decode(""" +MIIBSQYJKoZIhvcNAQcCoIIBOjCCATYCAQExDjAMBggqhQMHAQECAwUAMBsGCSqG +SIb3DQEHAaAOBAxUZXN0IG1lc3NhZ2UxggECMIH/AgEBMFswVjEpMCcGCSqGSIb3 +DQEJARYaR29zdFIzNDEwLTIwMTJAZXhhbXBsZS5jb20xKTAnBgNVBAMTIEdvc3RS +MzQxMC0yMDEyICg1MTIgYml0KSBleGFtcGxlAgEBMAwGCCqFAwcBAQIDBQAwDAYI +KoUDBwEBAQIFAASBgFyVohNhMHUi/+RAF3Gh/cC7why6v+4jPWVlx1TYlXtV8Hje +hI2Y+rP52/LO6EUHG/XcwCBbUxmRWsbUSRRBAexmaafkSdvv2FFwC8kHOcti+UPX +PS+KRYxT8vhcsBLWWxDkc1McI7aF09hqtED36mQOfACzeJjEoUjALpmJob1V + """) + prv_key_raw = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1] + self.process_cms( + content_info_raw, + prv_key_raw, + "id-tc26-gost-3410-12-512-paramSetB", + GOST34112012512, + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestDigested(TestCase): + """DigestedData test vectors from "Использование + алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 в + криптографических сообщениях формата CMS" (TK26CMS.pdf) + """ + + def process_cms(self, content_info_raw, hasher): + content_info, tail = ContentInfo().decode(content_info_raw) + self.assertSequenceEqual(tail, b"") + self.assertIsNotNone(content_info["content"].defined) + _, digested_data = content_info["content"].defined + self.assertSequenceEqual( + hasher(bytes(digested_data["encapContentInfo"]["eContent"])).digest(), + bytes(digested_data["digest"]), + ) + + def test_256(self): + content_info_raw = b64decode(""" +MIGdBgkqhkiG9w0BBwWggY8wgYwCAQAwDAYIKoUDBwEBAgIFADBXBgkqhkiG9w0B +BwGgSgRI0eUg4uXy8OgsINHy8Ojh7uboIOLt8/boLCDi5f7y+iDxIOzu8P8g8fLw +5evg7Ogg7eAg9fDg4fD7/yDv6/rq+yDI4+7w5eL7BCCd0v5OkECeXah/U5dtdAWw +wMrGKPxmmnQdUAY8VX6PUA== + """) + self.process_cms(content_info_raw, GOST34112012256) + + def test_512(self): + content_info_raw = b64decode(""" +MIG0BgkqhkiG9w0BBwWggaYwgaMCAQAwDAYIKoUDBwEBAgMFADBOBgkqhkiG9w0B +BwGgQQQ/MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEyMzQ1Njc4OTAx +MjM0NTY3ODkwMTIzNDU2Nzg5MDEyBEAbVNAaSvW51cw9htaNKFRisZq8JHUiLzXA +hRIr5Lof+gCtMPh2ezqCOExldPAkwxHipIEzKwjvf0F5eJHBZG9I + """) + self.process_cms(content_info_raw, GOST34112012512) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestEnvelopedKTRI(TestCase): + """EnvelopedData KeyTransRecipientInfo-based test vectors from + "Использование алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 + в криптографических сообщениях формата CMS" (TK26CMS.pdf) + """ + + def process_cms( + self, + content_info_raw, + prv_key_our, + curve_name, + keker, + plaintext_expected, + ): + sbox = "id-tc26-gost-28147-param-Z" + content_info, tail = ContentInfo().decode(content_info_raw, ctx={ + "defines_by_path": [ + ( + ( + "content", + DecodePathDefBy(id_envelopedData), + "recipientInfos", + any, + "ktri", + "encryptedKey", + DecodePathDefBy(spki_algorithm), + "transportParameters", + "ephemeralPublicKey", + "algorithm", + "algorithm", + ), + ( + ( + ("..", "subjectPublicKey"), + { + id_tc26_gost3410_2012_256: OctetString(), + id_tc26_gost3410_2012_512: OctetString(), + }, + ), + ), + ) for spki_algorithm in ( + id_tc26_gost3410_2012_256, + id_tc26_gost3410_2012_512, + ) + ], + }) + self.assertSequenceEqual(tail, b"") + self.assertIsNotNone(content_info["content"].defined) + _, enveloped_data = content_info["content"].defined + eci = enveloped_data["encryptedContentInfo"] + ri = enveloped_data["recipientInfos"][0] + self.assertIsNotNone(ri["ktri"]["encryptedKey"].defined) + _, encrypted_key = ri["ktri"]["encryptedKey"].defined + ukm = bytes(encrypted_key["transportParameters"]["ukm"]) + spk = encrypted_key["transportParameters"]["ephemeralPublicKey"]["subjectPublicKey"] + self.assertIsNotNone(spk.defined) + _, pub_key_their = spk.defined + curve = CURVES[curve_name] + kek = keker(curve, prv_key_our, bytes(pub_key_their), ukm) + key_wrapped = bytes(encrypted_key["sessionEncryptedKey"]["encryptedKey"]) + mac = bytes(encrypted_key["sessionEncryptedKey"]["macKey"]) + cek = unwrap_cryptopro(kek, ukm + key_wrapped + mac, sbox=sbox) + ciphertext = bytes(eci["encryptedContent"]) + self.assertIsNotNone(eci["contentEncryptionAlgorithm"]["parameters"].defined) + _, encryption_params = eci["contentEncryptionAlgorithm"]["parameters"].defined + iv = bytes(encryption_params["iv"]) + self.assertSequenceEqual( + cfb_decrypt(cek, ciphertext, iv, sbox=sbox, mesh=True), + plaintext_expected, + ) + + def test_256(self): + content_info_raw = b64decode(""" +MIIKGgYJKoZIhvcNAQcDoIIKCzCCCgcCAQAxggE0MIIBMAIBADBbMFYxKTAnBgkq +hkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBH +b3N0UjM0MTAtMjAxMiAyNTYgYml0cyBleGNoYW5nZQIBATAfBggqhQMHAQEBATAT +BgcqhQMCAiQABggqhQMHAQECAgSBrDCBqTAoBCCVJxUMdbKRzCJ5K1NWJIXnN7Ul +zaceeFlblA2qH4wZrgQEsHnIG6B9BgkqhQMHAQIFAQGgZjAfBggqhQMHAQEBATAT +BgcqhQMCAiQABggqhQMHAQECAgNDAARAFoqoLg1lV780co6GdwtjLtS4KCXv9VGR +sd7PTPHCT/5iGbvOlKNW2I8UhayJ0dv7RV7Nb1lDIxPxf4Mbp2CikgQI1b4+WpGE +sfQwggjIBgkqhkiG9w0BBwEwHwYGKoUDAgIVMBUECHYNkdvFoYdyBgkqhQMHAQIF +AQGAggiYvFFpJKILAFdXjcdLLYv4eruXzL/wOXL8y9HHIDMbSzV1GM033J5Yt/p4 +H6JYe1L1hjAfE/BAAYBndof2sSUxC3/I7xj+b7M8BZ3GYPqATPtR4aCQDK6z91lx +nDBAWx0HdsStT5TOj/plMs4zJDadvIJLfjmGkt0Np8FSnSdDPOcJAO/jcwiOPopg ++Z8eIuZNmY4seegTLue+7DGqvqi1GdZdMnvXBFIKc9m5DUsC7LdyboqKImh6giZE +YZnxb8a2naersPylhrf+zp4Piwwv808yOrD6LliXUiH0RojlmuaQP4wBkb7m073h +MeAWEWSvyXzOvOOuFST/hxPEupiTRoHPUdfboJT3tNpizUhE384SrvXHpwpgivQ4 +J0zF2/uzTBEupXR6dFC9rTHAK3X79SltqBNnHyIXBwe+BMqTmKTfnlPVHBUfTXZg +oakDItwKwa1MBOZeciwtUFza+7o9FZhKIandb848chGdgd5O9ksaXvPJDIPxQjZd +EBVhnXLlje4TScImwTdvYB8GsI8ljKb2bL3FjwQWGbPaOjXc2D9w+Ore8bk1E4TA +ayhypU7MH3Mq1EBZ4j0iROEFBQmYRZn8vAKZ0K7aPxcDeAnKAJxdokqrMkLgI6WX +0glh/3Cs9dI+0D2GqMSygauKCD0vTIo3atkEQswDZR4pMx88gB4gmx7iIGrc/ZXs +ZqHI7NQqeKtBwv2MCIj+/UTqdYDqbaniDwdVS8PE9nQnNU4gKffq3JbT+wRjJv6M +Dr231bQHgAsFTVKbZgoL4gj4V7bLQUmW06+W1BQUJ2+Sn7fp+Xet9Xd3cGtNdxzQ +zl6sGuiOlTNe0bfKP7QIMC7ekjflLBx8nwa2GZG19k3O0Z9JcDdN/kz6bGpPNssY +AIOkTvLQjxIM9MhRqIv6ee0rowTWQPwXJP7yHApop4XZvVX6h9gG2gazqbDej2lo +tAcfRAKj/LJ/bk9+OlNXOXVCKnwE1kXxZDsNJ51GdCungC56U/hmd3C1RhSLTpEc +FlOWgXKNjbn6SQrlq1yASKKr80T0fL7PFoYwKZoQbKMAVZQC1VBWQltHkEzdL73x +FwgZULNfdflF8sEhFC/zsVqckD/UnhzJz88PtCslMArJ7ntbEF1GzsSSfRfjBqnl +kSUreE5XX6+c9yp5HcJBiMzp6ZqqWWaED5Y5xp1hZeYjuKbDMfY4tbWVc7Hy0dD2 +KGfZLp5umqvPNs7aVBPmvuxtrnxcJlUB8u2HoiHc6/TuhrpaopYGBhxL9+kezuLR +v18nsAg8HOmcCNUS46NXQj/Mdpx8W+RsyzCQkJjieT/Yed20Zxq1zJoXIS0xAaUH +TdE2dWqiT6TGlh/KQYk3KyFPNnDmzJm04a2VWIwpp4ypXyxrB7XxnVY6Q4YBYbZs +FycxGjJWqj7lwc+lgZ8YV2WJ4snEo2os8SsA2GFWcUMiVTHDnEJvphDHmhWsf26A +bbRqwaRXNjhj05DamTRsczgvfjdl1pk4lJYE4ES3nixtMe4s1X8nSmM4KvfyVDul +J8uTpw1ZFnolTdfEL63BSf4FREoEqKB7cKuD7cpn7Rg4kRdM0/BLZGuxkH+pGMsI +Bb8LecUWyjGsI6h74Wz/U2uBrfgdRqhR+UsfB2QLaRgM6kCXZ4vM0auuzBViFCwK +tYMHzZWWz8gyVtJ0mzt1DrHCMx4pTS4yOhv4RkXBS/rub4VhVIsOGOGar5ZYtH47 +uBbdw3NC05JIFM7lI31d0s1fvvkTUR7eaqRW+SnR2c2oHpWlSO+Q0mrzx+vvOTdj +xa713YtklBvyUUQr2SIbsXGpFnwjn+sXK1onAavp/tEax8sNZvxg5yeseFcWn+gD +4rjk9FiSd1wp4fTDQFJ19evqruqKlq6k18l/ZAyUcEbIWSz2s3HfAAoAQyFPX1Q2 +95gVhRRw6lP4S6VPCfn/f+5jV4TcT6W/giRaHIk9Hty+g8bx1bFXaKVkQZ5R2Vmk +qsZ65ZgCrYQJmcErPmYybvP7NBeDS4AOSgBQAGMQF4xywdNm6bniWWo3N/xkFv32 +/25x8okGgD8QcYKmhzieLSSzOvM/exB14RO84YZOkZzm01Jll0nac/LEazKoVWbn +0VdcQ7pYEOqeMBXipsicNVYA/uhonp6op9cpIVYafPr0npCGwwhwcRuOrgSaZyCn +VG2tPkEOv9LKmUbhnaDA2YUSzOOjcCpIVvTSBnUEiorYpfRYgQLrbcd2qhVvNCLX +8ujZfMqXQXK8n5BK8JxNtczvaf+/2dfv1dQl0lHEAQhbNcsJ0t5GPhsSCC5oMBJl +ZJuOEO/8PBWKEnMZOM+Dz7gEgsBhGyMFFrKpiwQRpyEshSD2QpnK6Lp0t5C8Za2G +lhyZsEr+93AYOb5mm5+z02B4Yq9+RpepvjoqVeq/2uywZNq9MS98zVgNsmpryvTZ +3HJHHB20u2jcVu0G3Nhiv22lD70JWCYFAOupjgVcUcaBxjxwUMAvgHg7JZqs6mC6 +tvTKwQ4NtDhoAhARlDeWSwCWb2vPH2H7Lmqokif1RfvJ0hrLzkJuHdWrzIYzXpPs ++v9XJxLvbdKi9KU1Halq9S8dXT1fvs9DJTpUV/KW7QkRsTQJhTJBkQ07WUSJ4gBS +Qp4efxSRNIfMj7DR6qLLf13RpIPTJO9/+gNuBIFcupWVfUL7tJZt8Qsf9eGwZfP+ +YyhjC8AyZjH4/9RzLHSjuq6apgw3Mzw0j572Xg6xDLMK8C3Tn/vrLOvAd96b9MkF +3+ZHSLW3IgOiy+1jvK/20CZxNWc+pey8v4zji1hI17iohsipX/uZKRxhxF6+Xn2R +UQp6qoxHAspNXgWQ57xg7C3+gmi4ciVr0fT9pg54ogcowrRH+I6wd0EpeWPbzfnQ +pRmMVN+YtRsrEHwH3ToQ/i4vrtgA+eONuKT2uKZFikxA+VNmeeGdhkgqETMihQ== + """) + prv_key_our = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1] + + def keker(curve, prv, pub, ukm): + return kek_34102012256( + curve, + prv_unmarshal(prv), + pub_unmarshal(pub), + ukm_unmarshal(ukm), + ) + + self.process_cms( + content_info_raw, + prv_key_our, + "id-GostR3410-2001-CryptoPro-XchA-ParamSet", + keker, + b"Test data to encrypt.\n" * 100, + ) + + def test_512(self): + content_info_raw = b64decode(""" +MIIB0gYJKoZIhvcNAQcDoIIBwzCCAb8CAQAxggF8MIIBeAIBADBbMFYxKTAnBgkq +hkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBH +b3N0UjM0MTAtMjAxMiA1MTIgYml0cyBleGNoYW5nZQIBATAhBggqhQMHAQEBAjAV +BgkqhQMHAQIBAgIGCCqFAwcBAQIDBIHyMIHvMCgEIIsYzbVLn33aLinQ7SLNA7y+ +Lrm02khqDCfXrNS9iiMhBATerS8zoIHCBgkqhQMHAQIFAQGggaowIQYIKoUDBwEB +AQIwFQYJKoUDBwECAQICBggqhQMHAQECAwOBhAAEgYAYiTVLKpSGaAvjJEDQ0hdK +qR/jek5Q9Q2pXC+NkOimQh7dpCi+wcaHlPcBk96hmpnOFvLaiokX8V6jqtBl5gdk +M40kOXv8kcDdTzEVKA/ZLxA8xanL+gTD6ZjaPsUu06nsA2MoMBWcHLUzueaP3bGT +/yHTV+Za5xdcQehag/lNBgQIvCw4uUl0XC4wOgYJKoZIhvcNAQcBMB8GBiqFAwIC +FTAVBAj+1QzaXaN9FwYJKoUDBwECBQEBgAyK54euw0sHhEVEkA0= + """) + prv_key_our = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1] + + def keker(curve, prv, pub, ukm): + return kek_34102012256( + curve, + prv_unmarshal(prv), + pub_unmarshal(pub), + ukm_unmarshal(ukm), + ) + + self.process_cms( + content_info_raw, + prv_key_our, + "id-tc26-gost-3410-12-512-paramSetB", + keker, + b"Test message", + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestEnvelopedKARI(TestCase): + """EnvelopedData KeyAgreeRecipientInfo-based test vectors from + "Использование алгоритмов ГОСТ 28147-89, ГОСТ Р 34.11 и ГОСТ Р 34.10 + в криптографических сообщениях формата CMS" (TK26CMS.pdf) + """ + + def process_cms( + self, + content_info_raw, + prv_key_our, + curve_name, + keker, + plaintext_expected, + ): + sbox = "id-tc26-gost-28147-param-Z" + content_info, tail = ContentInfo().decode(content_info_raw, ctx={ + "defines_by_path": [ + ( + ( + "content", + DecodePathDefBy(id_envelopedData), + "recipientInfos", + any, + "kari", + "originator", + "originatorKey", + "algorithm", + "algorithm", + ), + ( + ( + ("..", "publicKey"), + { + id_tc26_gost3410_2012_256: OctetString(), + id_tc26_gost3410_2012_512: OctetString(), + }, + ), + ), + ) for _ in ( + id_tc26_gost3410_2012_256, + id_tc26_gost3410_2012_512, + ) + ], + }) + self.assertSequenceEqual(tail, b"") + self.assertIsNotNone(content_info["content"].defined) + _, enveloped_data = content_info["content"].defined + eci = enveloped_data["encryptedContentInfo"] + kari = enveloped_data["recipientInfos"][0]["kari"] + self.assertIsNotNone(kari["originator"]["originatorKey"]["publicKey"].defined) + _, pub_key_their = kari["originator"]["originatorKey"]["publicKey"].defined + ukm = bytes(kari["ukm"]) + rek = kari["recipientEncryptedKeys"][0] + curve = CURVES[curve_name] + kek = keker(curve, prv_key_our, bytes(pub_key_their), ukm) + self.assertIsNotNone(rek["encryptedKey"].defined) + _, encrypted_key = rek["encryptedKey"].defined + key_wrapped = bytes(encrypted_key["encryptedKey"]) + mac = bytes(encrypted_key["macKey"]) + cek = unwrap_gost(kek, ukm + key_wrapped + mac, sbox=sbox) + ciphertext = bytes(eci["encryptedContent"]) + self.assertIsNotNone(eci["contentEncryptionAlgorithm"]["parameters"].defined) + _, encryption_params = eci["contentEncryptionAlgorithm"]["parameters"].defined + iv = bytes(encryption_params["iv"]) + self.assertSequenceEqual( + cfb_decrypt(cek, ciphertext, iv, sbox=sbox, mesh=True), + plaintext_expected, + ) + + def test_256(self): + content_info_raw = b64decode(""" +MIIBhgYJKoZIhvcNAQcDoIIBdzCCAXMCAQIxggEwoYIBLAIBA6BooWYwHwYIKoUD +BwEBAQEwEwYHKoUDAgIkAAYIKoUDBwEBAgIDQwAEQPAdWM4pO38iZ49UjaXQpq+a +jhTa4KwY4B9TFMK7AiYmbFKE0eX/wvu69kFMQ2o3OJTnMOlr1WHiPYOmNO6C5hOh +CgQIX+vNomZakEIwIgYIKoUDBwEBAQEwFgYHKoUDAgINADALBgkqhQMHAQIFAQEw +gYwwgYkwWzBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxl +LmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgMjU2IGJpdHMgZXhjaGFuZ2UC +AQEEKjAoBCCNhrZOr7x2fsjjQAeDMv/tSoNRQSSQzzxgqdnYxJ3fIAQEgYLqVDA6 +BgkqhkiG9w0BBwEwHwYGKoUDAgIVMBUECHVmR/S+hlYiBgkqhQMHAQIFAQGADEI9 +UNjyuY+54uVcHw== + """) + prv_key_our = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1] + + def keker(curve, prv, pub, ukm): + return kek_34102012256( + curve, + prv_unmarshal(prv), + pub_unmarshal(pub), + ukm_unmarshal(ukm), + ) + + self.process_cms( + content_info_raw, + prv_key_our, + "id-GostR3410-2001-CryptoPro-XchA-ParamSet", + keker, + b"Test message", + ) + + def test_512(self): + content_info_raw = b64decode(""" +MIIBzAYJKoZIhvcNAQcDoIIBvTCCAbkCAQIxggF2oYIBcgIBA6CBraGBqjAhBggq +hQMHAQEBAjAVBgkqhQMHAQIBAgIGCCqFAwcBAQIDA4GEAASBgCB0nQy/Ljva/mRj +w6o+eDKIvnxwYIQB5XCHhZhCpHNZiWcFxFpYXZLWRPKifOxV7NStvqGE1+fkfhBe +btkQu0tdC1XL3LO2Cp/jX16XhW/IP5rKV84qWr1Owy/6tnSsNRb+ez6IttwVvaVV +pA6ONFy9p9gawoC8nitvAVJkWW0PoQoECDVfxzxgMTAHMCIGCCqFAwcBAQECMBYG +ByqFAwICDQAwCwYJKoUDBwECBQEBMIGMMIGJMFswVjEpMCcGCSqGSIb3DQEJARYa +R29zdFIzNDEwLTIwMTJAZXhhbXBsZS5jb20xKTAnBgNVBAMTIEdvc3RSMzQxMC0y +MDEyIDUxMiBiaXRzIGV4Y2hhbmdlAgEBBCowKAQg8C/OcxRR0Uq8nDjHrQlayFb3 +WFUZEnEuAKcuG6dTOawEBLhi9hIwOgYJKoZIhvcNAQcBMB8GBiqFAwICFTAVBAiD +1wH+CX6CwgYJKoUDBwECBQEBgAzUvQI4H2zRfgNgdlY= + """) + prv_key_our = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1] + + def keker(curve, prv, pub, ukm): + return kek_34102012256( + curve, + prv_unmarshal(prv), + pub_unmarshal(pub), + ukm_unmarshal(ukm), + ) + + self.process_cms( + content_info_raw, + prv_key_our, + "id-tc26-gost-3410-12-512-paramSetB", + keker, + b"Test message", + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestR132356510252019(TestCase): + """Test vectors from Р 1323565.1.025-2019 + """ + def setUp(self): + self.curve256 = CURVES["id-tc26-gost-3410-2012-256-paramSetA"] + self.curve512 = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + self.psk = hexdec("8F5EEF8814D228FB2BBC5612323730CFA33DB7263CC2C0A01A6C6953F33D61D5")[::-1] + + self.ca_prv = prv_unmarshal(hexdec("092F8D059E97E22B90B1AE99F0087FC4D26620B91550CBB437C191005A290810")[::-1]) + self.ca_pub = public_key(self.curve256, self.ca_prv) + self.ca_cert = Certificate().decod(b64decode(""" +MIIB8DCCAZ2gAwIBAgIEAYy6gTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYD +VQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwaDAhBggqhQMHAQEB +ATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MABEAaSoKcjw54UACci6svELNF0IYM +RIW8urUsqamIpoG46XCqrVOuI6Q13N4dwcRsbZdqByf+GC2f5ZfO3baN5bTKo4GF +MIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJjsCecS2npzESoTowODENMAsGA1UE +ChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0 +ggQBjLqBMB0GA1UdDgQWBBSA2Qz3mfhmTZNTiY7AnnEtp6cxEjAKBggqhQMHAQED +AgNBAAgv248F4OeNCkhlzJWec0evHYnMBlSzk1lDm0F875B7CqMrKh2MtJHXenbj +Gc2uRn2IwgmSf/LZDrYsKKqZSxk= +""")) + + self.sender256_prv = prv_unmarshal(hexdec("0B20810E449978C7C3B76C6FF77A16C532421139344A058EF56310B6B6F377E8")[::-1]) + self.sender256_pub = public_key(self.curve256, self.sender256_prv) + self.sender256_cert = Certificate().decod(b64decode(""" +MIIB8zCCAaCgAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD +VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwaDAhBggqhQMH +AQEBATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MABECWKQ0TYllqg4GmY3tBJiyz +pXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ6VbT +o4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJjsCecS2npzESoTowODENMAsG +A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt +Yml0ggQBjLqBMB0GA1UdDgQWBBTRnChHSWbQYwnJC62n2zu5Njd03zAKBggqhQMH +AQEDAgNBAB41oijaXSEn58l78y2rhxY35/lKEq4XWZ70FtsNlVxWATyzgO5Wliwn +t1O4GoZsxx8r6T/i7VG65UNmQlwdOKQ= +""")) + + self.recipient256_prv = prv_unmarshal(hexdec("0DC8DC1FF2BC114BABC3F1CA8C51E4F58610427E197B1C2FBDBA4AE58CBFB7CE")[::-1]) + self.recipient256_pub = public_key(self.curve256, self.recipient256_prv) + self.recipient256_cert = Certificate().decod(b64decode(""" +MIIB8jCCAZ+gAwIBAgIEAYy6gzAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD +VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDBoMCEGCCqFAwcB +AQEBMBUGCSqFAwcBAgEBAQYIKoUDBwEBAgIDQwAEQL8nghlzLGMKWHuWhNMPMN5u +L6SkGqRiJ6qZxZb+4dPKbBT9LNVvNKtwUed+BeE5kfqOfolPgFusnL1rnO9yREOj +gYUwgYIwYQYDVR0BBFowWIAUgNkM95n4Zk2TU4mOwJ5xLaenMRKhOjA4MQ0wCwYD +VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1i +aXSCBAGMuoEwHQYDVR0OBBYEFLue+PUb9Oe+pziBU+MvNejjgrzFMAoGCCqFAwcB +AQMCA0EAPP9Oad1/5jwokSjPpccsQ0xCdVYM+mGQ0IbpiZxQj8gnkt8sq4jR6Ya+ +I/BDkbZNDNE27TU1p3t5rE9NMEeViA== +""")) + + self.sender512_prv = prv_unmarshal(hexdec("F95A5D44C5245F63F2E7DF8E782C1924EADCB8D06C52D91023179786154CBDB1561B4DF759D69F67EE1FBD5B68800E134BAA12818DA4F3AC75B0E5E6F9256911")[::-1]) + self.sender512_pub = public_key(self.curve512, self.sender512_prv) + self.sender512_cert = Certificate().decod(b64decode(""" +MIICNjCCAeOgAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD +VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQwgaowIQYIKoUD +BwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwOBhAAEgYC0i7davCkOGGVcYqFP +tS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDPs2Sqa13ZN+Ts +/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo6xGunecH1/G4 +hMts9HYLnxbwJDMNVGuIHV6gzqOBhTCBgjBhBgNVHQEEWjBYgBSA2Qz3mfhmTZNT +iY7AnnEtp6cxEqE6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6 +IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUK+l9HAscONGx +zCcRpxRAmFHvlXowCgYIKoUDBwEBAwIDQQAbjA0Q41/rIKOOvjHKsAsoEJM+WJf6 +/PKXg2JaStthmw99bdtwwkU/qDbcje2tF6mt+XWyQBXwvfeES1GFY9fJ +""")) + + self.recipient512_prv = prv_unmarshal(hexdec("A50315981F0A7C7FC05B4EB9591A62B1F84BD6FD518ACFCEDF0A7C9CF388D1F18757C056ADA5B38CBF24CDDB0F1519EF72DB1712CEF1920952E94AF1F9C575DC")[::-1]) + self.recipient512_pub = public_key(self.curve512, self.recipient512_prv) + self.recipient512_cert = Certificate().decod(b64decode(""" +MIICNTCCAeKgAwIBAgIEAYy6hTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 +MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw +MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD +VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgNTEyLWJpdDCBqjAhBggqhQMH +AQEBAjAVBgkqhQMHAQIBAgEGCCqFAwcBAQIDA4GEAASBgKauwGYvUkzz19g0LP/p +zeRdmwy1m+QSy9W5ZrL/AGuJofm2ARjz40ozNbW6bp9hkHu8x66LX7u5zz+QeS2+ +X5om18UXriComgO0+qhZbc+Hzu0eQ8FjOd8LpLk3TzzfBltfLOX5IiPLjeum+pSP +0QjoXAVcrop//B4yvZIukvROo4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJ +jsCecS2npzESoTowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjog +R09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBSrXT5VKhm/5uff +kwW0XpG19k6AajAKBggqhQMHAQEDAgNBAAJBpsHRrQKZGb22LOzaReEB8rl2MbIR +ja64NaM5h+cAFoHm6t/k+ziLh2A11rTakR+5of4NQ3EjEhuPtomP2tc= +""")) + + def test_certs(self): + """Certificates signatures + """ + for prv, pub, curve, cert in ( + (self.ca_prv, self.ca_pub, self.curve256, self.ca_cert), + (self.sender256_prv, self.sender256_pub, self.curve256, self.sender256_cert), + (self.recipient256_prv, self.recipient256_pub, self.curve256, self.recipient256_cert), + (self.sender512_prv, self.sender512_pub, self.curve512, self.sender512_cert), + (self.recipient512_prv, self.recipient512_pub, self.curve512, self.recipient512_cert), + ): + pub_our = public_key(curve, prv) + self.assertEqual(pub_our, pub) + self.assertSequenceEqual( + pub_marshal(pub_our), + bytes(OctetString().decod(bytes( + cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"] + ))), + ) + + for cert in ( + self.ca_cert, + self.sender256_cert, + self.recipient256_cert, + self.sender512_cert, + self.recipient512_cert, + ): + self.assertTrue(verify( + self.curve256, + self.ca_pub, + GOST34112012256(cert["tbsCertificate"].encode()).digest()[::-1], + bytes(cert["signatureValue"]), + )) + + def test_signed_with_attrs(self): + ci = ContentInfo().decod(b64decode(""" +MIIENwYJKoZIhvcNAQcCoIIEKDCCBCQCAQExDDAKBggqhQMHAQECAzA7BgkqhkiG +9w0BBwGgLgQsyu7t8vDu6/zt++kg7/Do7OXwIOTr/yDx8vDz6vLz8PsgU2lnbmVk +RGF0YS6gggI6MIICNjCCAeOgAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYD +VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1i +aXQwHhcNMDEwMTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRU +SzI2MSowKAYDVQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQw +gaowIQYIKoUDBwEBAQIwFQYJKoUDBwECAQIBBggqhQMHAQECAwOBhAAEgYC0i7da +vCkOGGVcYqFPtS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDP +s2Sqa13ZN+Ts/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo +6xGunecH1/G4hMts9HYLnxbwJDMNVGuIHV6gzqOBhTCBgjBhBgNVHQEEWjBYgBSA +2Qz3mfhmTZNTiY7AnnEtp6cxEqE6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMT +HkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQU +K+l9HAscONGxzCcRpxRAmFHvlXowCgYIKoUDBwEBAwIDQQAbjA0Q41/rIKOOvjHK +sAsoEJM+WJf6/PKXg2JaStthmw99bdtwwkU/qDbcje2tF6mt+XWyQBXwvfeES1GF +Y9fJMYIBlDCCAZACAQEwQDA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBU +SzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQCBAGMuoQwCgYIKoUDBwEBAgOgga0w +GAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTkwMzIw +MTk1NTIyWjAiBgkqhkiG9w0BCWIxFQQTU2lnbmVkIGF0dHIncyB2YWx1ZTBPBgkq +hkiG9w0BCQQxQgRAUdPHEukF5BIfo9DoQIMdnB0ZLkzq0RueEUZSNv07A7C+GKWi +G62fueArg8uPCHPTUN6d/42p33fgMkEwH7f7cDAKBggqhQMHAQEBAgSBgGUnVka8 +FvTlClmOtj/FUUacBdE/nEBeMLOO/535VDYrXlftPE6zQf/4ghS7TQG2VRGQ3GWD ++L3+W09A7d5uyyTEbvgtdllUG0OyqFwKmJEaYsMin87SFVs0cn1PGV1fOKeLluZa +bLx5whxd+mzlpekL5i6ImRX+TpERxrA/xSe5 +""")) + _, sd = ci["content"].defined + content = bytes(sd["encapContentInfo"]["eContent"]) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры SignedData."), + ) + si = sd["signerInfos"][0] + self.assertEqual( + si["digestAlgorithm"]["algorithm"], + id_tc26_gost3411_2012_512, + ) + digest = [ + bytes(attr["attrValues"][0].defined[1]) for attr in si["signedAttrs"] + if attr["attrType"] == id_messageDigest + ][0] + self.assertSequenceEqual(digest, GOST34112012512(content).digest()) + self.assertTrue(verify( + self.curve512, + self.sender512_pub, + GOST34112012512( + SignedAttributes(si["signedAttrs"]).encode() + ).digest()[::-1], + bytes(si["signature"]), + )) + + def test_signed_without_attrs(self): + ci = ContentInfo().decod(b64decode(""" +MIIDAQYJKoZIhvcNAQcCoIIC8jCCAu4CAQExDDAKBggqhQMHAQECAjA7BgkqhkiG +9w0BBwGgLgQsyu7t8vDu6/zt++kg7/Do7OXwIOTr/yDx8vDz6vLz8PsgU2lnbmVk +RGF0YS6gggH3MIIB8zCCAaCgAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYD +VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1i +aXQwHhcNMDEwMTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRU +SzI2MSowKAYDVQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQw +aDAhBggqhQMHAQEBATAVBgkqhQMHAQIBAQEGCCqFAwcBAQICA0MABECWKQ0TYllq +g4GmY3tBJiyzpXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZ +huJaJfqZ6VbTo4GFMIGCMGEGA1UdAQRaMFiAFIDZDPeZ+GZNk1OJjsCecS2npzES +oTowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4x +MC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBTRnChHSWbQYwnJC62n2zu5Njd0 +3zAKBggqhQMHAQEDAgNBAB41oijaXSEn58l78y2rhxY35/lKEq4XWZ70FtsNlVxW +ATyzgO5Wliwnt1O4GoZsxx8r6T/i7VG65UNmQlwdOKQxgaIwgZ8CAQEwQDA4MQ0w +CwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1 +Ni1iaXQCBAGMuoIwCgYIKoUDBwEBAgIwCgYIKoUDBwEBAQEEQC6jZPA59szL9FiA +0wC71EBE42ap6gKxklT800cu2FvbLu972GJYNSI7+UeanVU37OVWyenEXi2E5HkU +94kBe8Q= +""")) + _, sd = ci["content"].defined + content = bytes(sd["encapContentInfo"]["eContent"]) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры SignedData."), + ) + si = sd["signerInfos"][0] + self.assertEqual( + si["digestAlgorithm"]["algorithm"], + id_tc26_gost3411_2012_256, + ) + self.assertTrue(verify( + self.curve256, + self.sender256_pub, + GOST34112012256(content).digest()[::-1], + bytes(si["signature"]), + )) + + def test_kari_ephemeral(self): + ci = ContentInfo().decod(b64decode(""" +MIIB/gYJKoZIhvcNAQcDoIIB7zCCAesCAQIxggFioYIBXgIBA6CBo6GBoDAXBggq +hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAe+itJVNbHM35RHfzuwFJPYdPXqtW +8hNEF7Z/XFEE2T71SRkhFX7ozYKQNh/TkVY9D4vG0LnD9Znr/pJyOjpsNb+dPcKX +Kbk/0JQxoPGHxFzASVAFq0ov/yBe2XGFWMeKUqtaAr7SvoYS0oEhT5EuT8BXmecd +nRe7NqOzESpb15ahIgQgsqHxOcdOp03l11S7k3OH1k1HNa5F8m9ctrOzH2846FMw +FwYJKoUDBwEBBwIBMAoGCCqFAwcBAQYCMHYwdDBAMDgxDTALBgNVBAoTBFRLMjYx +JzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdAIEAYy6hQQw +SxLc18zMwzLwXbcKqYhV/VzsdBgVArOHsSBIbaThJWE7zI37VGPMQJM5VXJ7GVcL +MF0GCSqGSIb3DQEHATAfBgkqhQMHAQEFAgIwEgQQ6EeVlADDCz2cdEWKy+tM94Av +yIFl/Ie4VeFFuczTsMsIaOUEe3Jn9GeVp8hZSj3O2q4hslQ/u/+Gj4QkSHm/M0ih +ITAfBgkqhQMHAQAGAQExEgQQs1t6D3J3WCEvxunnEE15NQ== +""")) + _, ed = ci["content"].defined + kari = ed["recipientInfos"][0]["kari"] + orig_key = kari["originator"]["originatorKey"] + self.assertEqual(orig_key["algorithm"]["algorithm"], id_tc26_gost3410_2012_512) + self.assertEqual( + GostR34102012PublicKeyParameters().decod( + bytes(orig_key["algorithm"]["parameters"]) + )["publicKeyParamSet"], + id_tc26_gost3410_2012_512_paramSetA, + ) + orig_pub = pub_unmarshal( + bytes(OctetString().decod(bytes(orig_key["publicKey"]))), + ) + ukm = bytes(kari["ukm"]) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_wrap_kexp15, + ) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_512, + ) + kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"]) + keymat = keg(self.curve512, self.recipient512_prv, orig_pub, ukm) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Kuznechik(kek).encrypt, + GOST3412Kuznechik(kim).encrypt, + GOST3412Kuznechik.blocksize, + kexp, + ukm[24:24 + GOST3412Kuznechik.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_ctracpkm_omac, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + self.assertEqual(ed["unprotectedAttrs"][0]["attrType"], id_cms_mac_attr) + encrypted_mac = bytes(ed["unprotectedAttrs"][0]["attrValues"][0].defined[1]) + encrypted_content = bytes(eci["encryptedContent"]) + cek_enc, cek_mac = kdf_tree_gostr3411_2012_256( + cek, b"kdf tree", eci_ukm[GOST3412Kuznechik.blocksize // 2:], 2, + ) + content_and_tag = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(cek_enc).encrypt, + 256 * 1024, + GOST3412Kuznechik.blocksize, + encrypted_content + encrypted_mac, + eci_ukm[:GOST3412Kuznechik.blocksize // 2], + ) + content = content_and_tag[:-GOST3412Kuznechik.blocksize] + tag_expected = content_and_tag[-GOST3412Kuznechik.blocksize:] + self.assertSequenceEqual( + omac( + GOST3412Kuznechik(cek_mac).encrypt, + GOST3412Kuznechik.blocksize, + content, + ), + tag_expected, + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EnvelopedData."), + ) + + def test_kari_static(self): + ci = ContentInfo().decod(b64decode(""" +MIIBawYJKoZIhvcNAQcDoIIBXDCCAVgCAQIxgfehgfQCAQOgQjBAMDgxDTALBgNV +BAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJp +dAIEAYy6gqEiBCBvcfyuSF57y8vVyaw8Z0ch3wjC4lPKTrpVRXty4Rhk5DAXBgkq +hQMHAQEHAQEwCgYIKoUDBwEBBgEwbjBsMEAwODENMAsGA1UEChMEVEsyNjEnMCUG +A1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0AgQBjLqDBChPbi6B +krXuLPexPAL2oUGCFWDGQHqINL5ExuMBG7/5XQRqriKARVa0MFkGCSqGSIb3DQEH +ATAbBgkqhQMHAQEFAQEwDgQMdNdCKnYAAAAwqTEDgC9O2bYyTGQJ8WUQGq0zHwzX +L0jFhWHTF1tcAxYmd9pX5i89UwIxhtYqyjX1QHju2g== +""")) + _, ed = ci["content"].defined + kari = ed["recipientInfos"][0]["kari"] + ukm = bytes(kari["ukm"]) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_wrap_kexp15, + ) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_256, + ) + kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"]) + keymat = keg( + self.curve256, + self.recipient256_prv, + self.sender256_pub, + ukm, + ) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Magma(kek).encrypt, + GOST3412Magma(kim).encrypt, + GOST3412Magma.blocksize, + kexp, + ukm[24:24 + GOST3412Magma.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_ctracpkm, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + content = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(cek).encrypt, + 8 * 1024, + GOST3412Magma.blocksize, + bytes(eci["encryptedContent"]), + eci_ukm[:GOST3412Magma.blocksize // 2], + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EnvelopedData."), + ) + + def test_ktri_256(self): + ci = ContentInfo().decod(b64decode(""" +MIIBlQYJKoZIhvcNAQcDoIIBhjCCAYICAQAxggEcMIIBGAIBADBAMDgxDTALBgNV +BAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJp +dAIEAYy6gzAXBgkqhQMHAQEHAgEwCgYIKoUDBwEBBgEEgbcwgbQEMFiMredFR3Mv +3g2wqyVXRnrhYEBMNFaqqgBpHwPQh3bF98tt9HZPxRDCww0OPfxeuTBeMBcGCCqF +AwcBAQEBMAsGCSqFAwcBAgEBAQNDAARAdFJ9ww+3ptvQiaQpizCldNYhl4DB1rl8 +Fx/2FIgnwssCbYRQ+UuRsTk9dfLLTGJG3JIEXKFxXWBgOrK965A5pAQg9f2/EHxG +DfetwCe1a6uUDCWD+wp5dYOpfkry8YRDEJgwXQYJKoZIhvcNAQcBMB8GCSqFAwcB +AQUCATASBBDUHNxmVclO/v3OaY9P7jxOgC+sD9CHGlEMRUpfGn6yfFDMExmYeby8 +LzdPJe1MkYV0qQgdC1zI3nQ7/4taf+4zRA== +""")) + _, ed = ci["content"].defined + ktri = ed["recipientInfos"][0]["ktri"] + self.assertEqual( + ktri["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_wrap_kexp15, + ) + self.assertEqual( + ktri["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_256, + ) + _, encrypted_key = ktri["encryptedKey"].defined + self.assertEqual( + encrypted_key["ephemeralPublicKey"]["algorithm"]["algorithm"], + id_tc26_gost3410_2012_256, + ) + pub = pub_unmarshal(bytes(OctetString().decod( + bytes(encrypted_key["ephemeralPublicKey"]["subjectPublicKey"]) + ))) + ukm = bytes(encrypted_key["ukm"]) + kexp = bytes(encrypted_key["encryptedKey"]) + keymat = keg(self.curve256, self.recipient256_prv, pub, ukm) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Kuznechik(kek).encrypt, + GOST3412Kuznechik(kim).encrypt, + GOST3412Kuznechik.blocksize, + kexp, + ukm[24:24 + GOST3412Kuznechik.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_ctracpkm, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + content = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(cek).encrypt, + 256 * 1024, + GOST3412Kuznechik.blocksize, + bytes(eci["encryptedContent"]), + eci_ukm[:GOST3412Kuznechik.blocksize // 2], + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EnvelopedData."), + ) + + def test_ktri_512(self): + ci = ContentInfo().decod(b64decode(""" +MIIB5wYJKoZIhvcNAQcDoIIB2DCCAdQCAQAxggFXMIIBUwIBADBAMDgxDTALBgNVBAoTBFRL +MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdAIEAYy6hTAXBgkq +hQMHAQEHAQEwCgYIKoUDBwEBBgIEgfIwge8EKDof9JLTJVuIfP+c+imDCGyOLtAYENkoXpeU +CdiGn0Lt65t3TN9G0bUwgaAwFwYIKoUDBwEBAQIwCwYJKoUDBwECAQIBA4GEAASBgDD9XXHn +0j4EwY3DGB1wzHeThPRDlCwIvpmqWy00QDhS3fLRWiETSe9uMLeg27zI/EiserKMasNZum/i +d09cmP8aTNIDNRtI5H9M0mH7LpEtY8L901MszvOKHLDYdemvz0JUqOvBtvoeQ6sV4Gl45zXx +HTzBWlBw1FLX/ITWLapaBCAa09foTeA+PObBznGuCOPoKy+xz/9IIVmZidI6EYkIrzBZBgkq +hkiG9w0BBwEwGwYJKoUDBwEBBQECMA4EDA4z1UwRL4WYzKFX/oAv8eEX3fWt6hxDpjO0rI7/ +CiJ/CwYGCKODJ9h63vAwlsWwcPwAjxcsLvCNlv6i4NqhGTAXBgkqhQMHAQAGAQExCgQIs2DT +LuZ22Yw= +""")) + _, ed = ci["content"].defined + ktri = ed["recipientInfos"][0]["ktri"] + self.assertEqual( + ktri["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_wrap_kexp15, + ) + self.assertEqual( + ktri["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_512, + ) + _, encrypted_key = ktri["encryptedKey"].defined + self.assertEqual( + encrypted_key["ephemeralPublicKey"]["algorithm"]["algorithm"], + id_tc26_gost3410_2012_512, + ) + pub = pub_unmarshal( + bytes(OctetString().decod( + bytes(encrypted_key["ephemeralPublicKey"]["subjectPublicKey"]) + )), + ) + ukm = bytes(encrypted_key["ukm"]) + kexp = bytes(encrypted_key["encryptedKey"]) + keymat = keg(self.curve512, self.recipient512_prv, pub, ukm) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Magma(kek).encrypt, + GOST3412Magma(kim).encrypt, + GOST3412Magma.blocksize, + kexp, + ukm[24:24 + GOST3412Magma.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_ctracpkm_omac, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + self.assertEqual(ed["unprotectedAttrs"][0]["attrType"], id_cms_mac_attr) + encrypted_mac = bytes(ed["unprotectedAttrs"][0]["attrValues"][0].defined[1]) + encrypted_content = bytes(eci["encryptedContent"]) + cek_enc, cek_mac = kdf_tree_gostr3411_2012_256( + cek, b"kdf tree", eci_ukm[GOST3412Magma.blocksize // 2:], 2, + ) + content_and_tag = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(cek_enc).encrypt, + 8 * 1024, + GOST3412Magma.blocksize, + encrypted_content + encrypted_mac, + eci_ukm[:GOST3412Magma.blocksize // 2], + ) + content = content_and_tag[:-GOST3412Magma.blocksize] + tag_expected = content_and_tag[-GOST3412Magma.blocksize:] + self.assertSequenceEqual( + omac( + GOST3412Magma(cek_mac).encrypt, + GOST3412Magma.blocksize, + content, + ), + tag_expected, + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EnvelopedData."), + ) + + def test_digested256(self): + ci = ContentInfo().decod(b64decode(""" +MH0GCSqGSIb3DQEHBaBwMG4CAQAwCgYIKoUDBwEBAgIwOwYJKoZIhvcNAQcBoC4ELMru7fLw +7uv87fvpIO/w6Ozl8CDk6/8g8fLw8+ry8/D7IERpZ2VzdERhdGEuBCD/esPQYsGkzxZV8uUM +IAWt6SI8KtxBP8NyG8AGbJ8i/Q== +""")) + _, dd = ci["content"].defined + eci = dd["encapContentInfo"] + self.assertSequenceEqual( + GOST34112012256(bytes(eci["eContent"])).digest(), + bytes(dd["digest"]), + ) + + def test_digested512(self): + ci = ContentInfo().decod(b64decode(""" +MIGfBgkqhkiG9w0BBwWggZEwgY4CAQAwCgYIKoUDBwEBAgMwOwYJKoZIhvcNAQcBoC4ELMru +7fLw7uv87fvpIO/w6Ozl8CDk6/8g8fLw8+ry8/D7IERpZ2VzdERhdGEuBEDe4VUvcKSRvU7R +FVhFjajXY+nJSUkUsoi3oOeJBnru4PErt8RusPrCJs614ciHCM+ehrC4a+M1Nbq77F/Wsa/v +""")) + _, dd = ci["content"].defined + eci = dd["encapContentInfo"] + self.assertSequenceEqual( + GOST34112012512(bytes(eci["eContent"])).digest(), + bytes(dd["digest"]), + ) + + def test_encrypted_kuznechik(self): + ci = ContentInfo().decod(b64decode(""" +MHEGCSqGSIb3DQEHBqBkMGICAQAwXQYJKoZIhvcNAQcBMB8GCSqFAwcBAQUCATASBBBSwX+z +yOEPPuGyfpsRG4AigC/P8ftTdQMStfIThVkE/vpJlwaHgGv83m2bsPayeyuqpoTeEMOaqGcO +0MxHWsC9hQ== +""")) + _, ed = ci["content"].defined + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_ctracpkm, + ) + ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + content = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(self.psk).encrypt, + 256 * 1024, + GOST3412Kuznechik.blocksize, + bytes(eci["encryptedContent"]), + ukm[:GOST3412Kuznechik.blocksize // 2], + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EncryptedData."), + ) + + def test_encrypted_magma(self): + ci = ContentInfo().decod(b64decode(""" +MIGIBgkqhkiG9w0BBwagezB5AgEAMFkGCSqGSIb3DQEHATAbBgkqhQMHAQEFAQIwDgQMuncO +u3uYPbI30vFCgC9Nsws4R09yLp6jUtadncWUPZGmCGpPKnXGgNHvEmUArgKJvu4FPHtLkHuL +eQXZg6EZMBcGCSqFAwcBAAYBATEKBAjCbQoH632oGA== +""")) + _, ed = ci["content"].defined + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_magma_ctracpkm_omac, + ) + ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + self.assertEqual(ed["unprotectedAttrs"][0]["attrType"], id_cms_mac_attr) + encrypted_mac = bytes(ed["unprotectedAttrs"][0]["attrValues"][0].defined[1]) + cek_enc, cek_mac = kdf_tree_gostr3411_2012_256( + self.psk, b"kdf tree", ukm[GOST3412Magma.blocksize // 2:], 2, + ) + content_and_tag = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(cek_enc).encrypt, + 8 * 1024, + GOST3412Magma.blocksize, + bytes(eci["encryptedContent"]) + encrypted_mac, + ukm[:GOST3412Magma.blocksize // 2], + ) + content = content_and_tag[:-GOST3412Magma.blocksize] + tag_expected = content_and_tag[-GOST3412Magma.blocksize:] + self.assertSequenceEqual( + omac( + GOST3412Magma(cek_mac).encrypt, + GOST3412Magma.blocksize, + content, + ), + tag_expected, + ) + self.assertEqual( + content.decode("cp1251"), + text_type(u"Контрольный пример для структуры EncryptedData."), + ) diff --git a/pygost-5.13/pygost/test_gost28147.py b/pygost-5.13/pygost/test_gost28147.py new file mode 100644 index 0000000..a83e930 --- /dev/null +++ b/pygost-5.13/pygost/test_gost28147.py @@ -0,0 +1,384 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from unittest import TestCase + +from pygost.gost28147 import block2ns +from pygost.gost28147 import BLOCKSIZE +from pygost.gost28147 import cbc_decrypt +from pygost.gost28147 import cbc_encrypt +from pygost.gost28147 import cfb_decrypt +from pygost.gost28147 import cfb_encrypt +from pygost.gost28147 import cnt +from pygost.gost28147 import DEFAULT_SBOX +from pygost.gost28147 import ecb_decrypt +from pygost.gost28147 import ecb_encrypt +from pygost.gost28147 import encrypt +from pygost.gost28147 import KEYSIZE +from pygost.gost28147 import MESH_MAX_DATA +from pygost.gost28147 import ns2block +from pygost.utils import hexdec +from pygost.utils import strxor + + +class ECBTest(TestCase): + def test_gcl(self): + """Test vectors from libgcl3 + """ + sbox = "id-Gost28147-89-TestParamSet" + key = hexdec(b"0475f6e05038fbfad2c7c390edb3ca3d1547124291ae1e8a2f79cd9ed2bcefbd") + plaintext = bytes(bytearray(( + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, + 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, + 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, + 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, + 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, + 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, + 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, + 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, + 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, + 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, + 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, + 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, + 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, + 0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, + 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, + 0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, + 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, + 0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8, + 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, + 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, + ))) + ciphertext = bytes(bytearray(( + 0x4b, 0x8c, 0x4c, 0x98, 0x15, 0xf2, 0x4a, 0xea, + 0x1e, 0xc3, 0x57, 0x09, 0xb3, 0xbc, 0x2e, 0xd1, + 0xe0, 0xd1, 0xf2, 0x22, 0x65, 0x2d, 0x59, 0x18, + 0xf7, 0xdf, 0xfc, 0x80, 0x4b, 0xde, 0x5c, 0x68, + 0x46, 0x53, 0x75, 0x53, 0xa7, 0x46, 0x0d, 0xec, + 0x05, 0x1f, 0x1b, 0xd3, 0x0a, 0x63, 0x1a, 0xb7, + 0x78, 0xc4, 0x43, 0xe0, 0x5d, 0x3e, 0xa4, 0x0e, + 0x2d, 0x7e, 0x23, 0xa9, 0x1b, 0xc9, 0x02, 0xbc, + 0x21, 0x0c, 0x84, 0xcb, 0x0d, 0x0a, 0x07, 0xc8, + 0x7b, 0xd0, 0xfb, 0xb5, 0x1a, 0x14, 0x04, 0x5c, + 0xa2, 0x53, 0x97, 0x71, 0x2e, 0x5c, 0xc2, 0x8f, + 0x39, 0x3f, 0x6f, 0x52, 0xf2, 0x30, 0x26, 0x4e, + 0x8c, 0xe0, 0xd1, 0x01, 0x75, 0x6d, 0xdc, 0xd3, + 0x03, 0x79, 0x1e, 0xca, 0xd5, 0xc1, 0x0e, 0x12, + 0x53, 0x0a, 0x78, 0xe2, 0x0a, 0xb1, 0x1c, 0xea, + 0x3a, 0xf8, 0x55, 0xb9, 0x7c, 0xe1, 0x0b, 0xba, + 0xa0, 0xc8, 0x96, 0xeb, 0x50, 0x5a, 0xd3, 0x60, + 0x43, 0xa3, 0x0f, 0x98, 0xdb, 0xd9, 0x50, 0x6d, + 0x63, 0x91, 0xaf, 0x01, 0x40, 0xe9, 0x75, 0x5a, + 0x46, 0x5c, 0x1f, 0x19, 0x4a, 0x0b, 0x89, 0x9b, + 0xc4, 0xf6, 0xf8, 0xf5, 0x2f, 0x87, 0x3f, 0xfa, + 0x26, 0xd4, 0xf8, 0x25, 0xba, 0x1f, 0x98, 0x82, + 0xfc, 0x26, 0xaf, 0x2d, 0xc0, 0xf9, 0xc4, 0x58, + 0x49, 0xfa, 0x09, 0x80, 0x02, 0x62, 0xa4, 0x34, + 0x2d, 0xcb, 0x5a, 0x6b, 0xab, 0x61, 0x5d, 0x08, + 0xd4, 0x26, 0xe0, 0x08, 0x13, 0xd6, 0x2e, 0x02, + 0x2a, 0x37, 0xe8, 0xd0, 0xcf, 0x36, 0xf1, 0xc7, + 0xc0, 0x3f, 0x9b, 0x21, 0x60, 0xbd, 0x29, 0x2d, + 0x2e, 0x01, 0x48, 0x4e, 0xf8, 0x8f, 0x20, 0x16, + 0x8a, 0xbf, 0x82, 0xdc, 0x32, 0x7a, 0xa3, 0x18, + 0x69, 0xd1, 0x50, 0x59, 0x31, 0x91, 0xf2, 0x6c, + 0x5a, 0x5f, 0xca, 0x58, 0x9a, 0xb2, 0x2d, 0xb2, + ))) + encrypted = ecb_encrypt(key, plaintext, sbox=sbox) + self.assertSequenceEqual(encrypted, ciphertext) + decrypted = ecb_decrypt(key, encrypted, sbox=sbox) + self.assertSequenceEqual(decrypted, plaintext) + + def test_cryptopp(self): + """Test vectors from Crypto++ 5.6.2 + """ + sbox = "AppliedCryptography" + data = ( + (b"BE5EC2006CFF9DCF52354959F1FF0CBFE95061B5A648C10387069C25997C0672", b"0DF82802B741A292", b"07F9027DF7F7DF89"), + (b"B385272AC8D72A5A8B344BC80363AC4D09BF58F41F540624CBCB8FDCF55307D7", b"1354EE9C0A11CD4C", b"4FB50536F960A7B1"), + (b"AEE02F609A35660E4097E546FD3026B032CD107C7D459977ADF489BEF2652262", b"6693D492C4B0CC39", b"670034AC0FA811B5"), + (b"320E9D8422165D58911DFC7D8BBB1F81B0ECD924023BF94D9DF7DCF7801240E0", b"99E2D13080928D79", b"8118FF9D3B3CFE7D"), + (b"C9F703BBBFC63691BFA3B7B87EA8FD5E8E8EF384EF733F1A61AEF68C8FFA265F", b"D1E787749C72814C", b"A083826A790D3E0C"), + (b"728FEE32F04B4C654AD7F607D71C660C2C2670D7C999713233149A1C0C17A1F0", b"D4C05323A4F7A7B5", b"4D1F2E6B0D9DE2CE"), + (b"35FC96402209500FCFDEF5352D1ABB038FE33FC0D9D58512E56370B22BAA133B", b"8742D9A05F6A3AF6", b"2F3BB84879D11E52"), + (b"D416F630BE65B7FE150656183370E07018234EE5DA3D89C4CE9152A03E5BFB77", b"F86506DA04E41CB8", b"96F0A5C77A04F5CE"), + ) + for key, pt, ct in data: + key = hexdec(key) + pt = hexdec(pt) + ct = hexdec(ct) + self.assertSequenceEqual(ecb_encrypt(key, pt, sbox=sbox), ct) + + def test_cryptomanager(self): + """Test vector from http://cryptomanager.com/tv.html + """ + sbox = "id-GostR3411-94-TestParamSet" + key = hexdec(b"75713134B60FEC45A607BB83AA3746AF4FF99DA6D1B53B5B1B402A1BAA030D1B") + self.assertSequenceEqual( + ecb_encrypt(key, hexdec(b"1122334455667788"), sbox=sbox), + hexdec(b"03251E14F9D28ACB"), + ) + + +class CFBTest(TestCase): + def test_cryptomanager(self): + """Test vector from http://cryptomanager.com/tv.html + """ + key = hexdec(b"75713134B60FEC45A607BB83AA3746AF4FF99DA6D1B53B5B1B402A1BAA030D1B") + sbox = "id-GostR3411-94-TestParamSet" + self.assertSequenceEqual( + cfb_encrypt( + key, + hexdec(b"112233445566778899AABBCCDD800000"), + iv=hexdec(b"0102030405060708"), + sbox=sbox, + ), + hexdec(b"6EE84586DD2BCA0CAD3616940E164242"), + ) + self.assertSequenceEqual( + cfb_decrypt( + key, + hexdec(b"6EE84586DD2BCA0CAD3616940E164242"), + iv=hexdec(b"0102030405060708"), + sbox=sbox, + ), + hexdec(b"112233445566778899AABBCCDD800000"), + ) + + def test_steps(self): + """Check step-by-step operation manually + """ + key = urandom(KEYSIZE) + iv = urandom(BLOCKSIZE) + plaintext = urandom(20) + ciphertext = cfb_encrypt(key, plaintext, iv) + + # First full block + step = encrypt(DEFAULT_SBOX, key, block2ns(iv)) + step = strxor(plaintext[:8], ns2block(step)) + self.assertSequenceEqual(step, ciphertext[:8]) + + # Second full block + step = encrypt(DEFAULT_SBOX, key, block2ns(step)) + step = strxor(plaintext[8:16], ns2block(step)) + self.assertSequenceEqual(step, ciphertext[8:16]) + + # Third non-full block + step = encrypt(DEFAULT_SBOX, key, block2ns(step)) + step = strxor(plaintext[16:] + 4 * b"\x00", ns2block(step)) + self.assertSequenceEqual(step[:4], ciphertext[16:]) + + def test_random(self): + """Random data with various sizes + """ + key = urandom(KEYSIZE) + iv = urandom(BLOCKSIZE) + for size in (5, 8, 16, 120): + pt = urandom(size) + self.assertSequenceEqual( + cfb_decrypt(key, cfb_encrypt(key, pt, iv), iv), + pt, + ) + + +class CTRTest(TestCase): + def test_gcl(self): + """Test vectors from libgcl3 + """ + sbox = "id-Gost28147-89-TestParamSet" + key = hexdec(b"0475f6e05038fbfad2c7c390edb3ca3d1547124291ae1e8a2f79cd9ed2bcefbd") + plaintext = bytes(bytearray(( + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, + 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0x37, 0x36, 0x35, 0x34, 0x33, 0x32, 0x31, 0x30, + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, + 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, + 0x4f, 0x4e, 0x4d, 0x4c, 0x4b, 0x4a, 0x49, 0x48, + 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, + 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, + 0x67, 0x66, 0x65, 0x64, 0x63, 0x62, 0x61, 0x60, + 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, + 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, + 0x7f, 0x7e, 0x7d, 0x7c, 0x7b, 0x7a, 0x79, 0x78, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, + 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, + 0x97, 0x96, 0x95, 0x94, 0x93, 0x92, 0x91, 0x90, + 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, + 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, + 0xaf, 0xae, 0xad, 0xac, 0xab, 0xaa, 0xa9, 0xa8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, + 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xc7, 0xc6, 0xc5, 0xc4, 0xc3, 0xc2, 0xc1, 0xc0, + 0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, + 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, + 0xdf, 0xde, 0xdd, 0xdc, 0xdb, 0xda, 0xd9, 0xd8, + 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, + 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, + 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, + ))) + ciphertext = bytes(bytearray(( + 0x4a, 0x5e, 0x37, 0x6c, 0xa1, 0x12, 0xd3, 0x55, + 0x09, 0x13, 0x1a, 0x21, 0xac, 0xfb, 0xb2, 0x1e, + 0x8c, 0x24, 0x9b, 0x57, 0x20, 0x68, 0x46, 0xd5, + 0x23, 0x2a, 0x26, 0x35, 0x12, 0x56, 0x5c, 0x69, + 0x2a, 0x2f, 0xd1, 0xab, 0xbd, 0x45, 0xdc, 0x3a, + 0x1a, 0xa4, 0x57, 0x64, 0xd5, 0xe4, 0x69, 0x6d, + 0xb4, 0x8b, 0xf1, 0x54, 0x78, 0x3b, 0x10, 0x8f, + 0x7a, 0x4b, 0x32, 0xe0, 0xe8, 0x4c, 0xbf, 0x03, + 0x24, 0x37, 0x95, 0x6a, 0x55, 0xa8, 0xce, 0x6f, + 0x95, 0x62, 0x12, 0xf6, 0x79, 0xe6, 0xf0, 0x1b, + 0x86, 0xef, 0x36, 0x36, 0x05, 0xd8, 0x6f, 0x10, + 0xa1, 0x41, 0x05, 0x07, 0xf8, 0xfa, 0xa4, 0x0b, + 0x17, 0x2c, 0x71, 0xbc, 0x8b, 0xcb, 0xcf, 0x3d, + 0x74, 0x18, 0x32, 0x0b, 0x1c, 0xd2, 0x9e, 0x75, + 0xba, 0x3e, 0x61, 0xe1, 0x61, 0x96, 0xd0, 0xee, + 0x8f, 0xf2, 0x9a, 0x5e, 0xb7, 0x7a, 0x15, 0xaa, + 0x4e, 0x1e, 0x77, 0x7c, 0x99, 0xe1, 0x41, 0x13, + 0xf4, 0x60, 0x39, 0x46, 0x4c, 0x35, 0xde, 0x95, + 0xcc, 0x4f, 0xd5, 0xaf, 0xd1, 0x4d, 0x84, 0x1a, + 0x45, 0xc7, 0x2a, 0xf2, 0x2c, 0xc0, 0xb7, 0x94, + 0xa3, 0x08, 0xb9, 0x12, 0x96, 0xb5, 0x97, 0x99, + 0x3a, 0xb7, 0x0c, 0x14, 0x56, 0xb9, 0xcb, 0x49, + 0x44, 0xa9, 0x93, 0xa9, 0xfb, 0x19, 0x10, 0x8c, + 0x6a, 0x68, 0xe8, 0x7b, 0x06, 0x57, 0xf0, 0xef, + 0x88, 0x44, 0xa6, 0xd2, 0x98, 0xbe, 0xd4, 0x07, + 0x41, 0x37, 0x45, 0xa6, 0x71, 0x36, 0x76, 0x69, + 0x4b, 0x75, 0x15, 0x33, 0x90, 0x29, 0x6e, 0x33, + 0xcb, 0x96, 0x39, 0x78, 0x19, 0x2e, 0x96, 0xf3, + 0x49, 0x4c, 0x89, 0x3d, 0xa1, 0x86, 0x82, 0x00, + 0xce, 0xbd, 0x54, 0x29, 0x65, 0x00, 0x1d, 0x16, + 0x13, 0xc3, 0xfe, 0x1f, 0x8c, 0x55, 0x63, 0x09, + 0x1f, 0xcd, 0xd4, 0x28, 0xca, + ))) + iv = b"\x02\x01\x01\x01\x01\x01\x01\x01" + encrypted = cnt(key, plaintext, iv=iv, sbox=sbox) + self.assertSequenceEqual(encrypted, ciphertext) + decrypted = cnt(key, encrypted, iv=iv, sbox=sbox) + self.assertSequenceEqual(decrypted, plaintext) + + def test_gcl2(self): + """Test vectors 2 from libgcl3 + """ + sbox = "id-Gost28147-89-TestParamSet" + key = hexdec(b"fc7ad2886f455b50d29008fa622b57d5c65b3c637202025799cadf0768519e8a") + plaintext = bytes(bytearray(( + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x1f, 0x1e, 0x1d, 0x1c, 0x1b, 0x1a, 0x19, 0x18, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, + 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, + ))) + ciphertext = bytes(bytearray(( + 0xd0, 0xbe, 0x60, 0x1a, 0x2c, 0xf1, 0x90, 0x26, + 0x9b, 0x7b, 0x23, 0xb4, 0xd2, 0xcc, 0xe1, 0x15, + 0xf6, 0x05, 0x57, 0x28, 0x88, 0x75, 0xeb, 0x1e, + 0xd3, 0x62, 0xdc, 0xda, 0x9b, 0x62, 0xee, 0x9a, + 0x57, 0x87, 0x8a, 0xf1, 0x82, 0x37, 0x9c, 0x7f, + 0x13, 0xcc, 0x55, 0x38, 0xb5, 0x63, 0x32, 0xc5, + 0x23, 0xa4, 0xcb, 0x7d, 0x51, + ))) + iv = BLOCKSIZE * b"\x00" + encrypted = cnt(key, plaintext, iv=iv, sbox=sbox) + self.assertSequenceEqual(encrypted, ciphertext) + decrypted = cnt(key, encrypted, iv=iv, sbox=sbox) + self.assertSequenceEqual(decrypted, plaintext) + + +class CBCTest(TestCase): + def test_pad_requirement(self): + key = KEYSIZE * b"x" + for s in (b"", b"foo", b"foobarbaz"): + with self.assertRaises(ValueError): + cbc_encrypt(key, s, pad=False) + with self.assertRaises(ValueError): + cbc_decrypt(key, s, pad=False) + + def test_passes(self): + iv = urandom(BLOCKSIZE) + key = KEYSIZE * b"x" + for pt in (b"foo", b"foobarba", b"foobarbaz", 16 * b"x"): + ct = cbc_encrypt(key, pt, iv) + dt = cbc_decrypt(key, ct) + self.assertSequenceEqual(pt, dt) + + def test_iv_existence_check(self): + key = KEYSIZE * b"x" + with self.assertRaises(ValueError): + cbc_decrypt(key, BLOCKSIZE * b"x") + iv = urandom(BLOCKSIZE) + cbc_decrypt(key, cbc_encrypt(key, BLOCKSIZE * b"x", iv)) + + def test_meshing(self): + pt = urandom(MESH_MAX_DATA * 3) + key = urandom(KEYSIZE) + ct = cbc_encrypt(key, pt) + dt = cbc_decrypt(key, ct) + self.assertSequenceEqual(pt, dt) + + +class CFBMeshingTest(TestCase): + def setUp(self): + self.key = urandom(KEYSIZE) + self.iv = urandom(BLOCKSIZE) + + def test_single(self): + pt = b"\x00" + ct = cfb_encrypt(self.key, pt, mesh=True) + dec = cfb_decrypt(self.key, ct, mesh=True) + self.assertSequenceEqual(pt, dec) + + def test_short(self): + pt = urandom(MESH_MAX_DATA - 1) + ct = cfb_encrypt(self.key, pt, mesh=True) + dec = cfb_decrypt(self.key, ct, mesh=True) + dec_plain = cfb_decrypt(self.key, ct) + self.assertSequenceEqual(pt, dec) + self.assertSequenceEqual(pt, dec_plain) + + def test_short_iv(self): + pt = urandom(MESH_MAX_DATA - 1) + ct = cfb_encrypt(self.key, pt, iv=self.iv, mesh=True) + dec = cfb_decrypt(self.key, ct, iv=self.iv, mesh=True) + dec_plain = cfb_decrypt(self.key, ct, iv=self.iv) + self.assertSequenceEqual(pt, dec) + self.assertSequenceEqual(pt, dec_plain) + + def test_longer_iv(self): + pt = urandom(MESH_MAX_DATA * 3) + ct = cfb_encrypt(self.key, pt, iv=self.iv, mesh=True) + dec = cfb_decrypt(self.key, ct, iv=self.iv, mesh=True) + dec_plain = cfb_decrypt(self.key, ct, iv=self.iv) + self.assertSequenceEqual(pt, dec) + self.assertNotEqual(pt, dec_plain) diff --git a/pygost-5.13/pygost/test_gost28147_mac.py b/pygost-5.13/pygost/test_gost28147_mac.py new file mode 100644 index 0000000..ec1af3f --- /dev/null +++ b/pygost-5.13/pygost/test_gost28147_mac.py @@ -0,0 +1,63 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from unittest import TestCase + +from pygost.gost28147_mac import MAC + + +class TestMAC(TestCase): + """Test vectors generated with libgcl3 library + """ + k = b"This is message\xFF length\x0032 bytes" + + def test_a(self): + self.assertSequenceEqual( + MAC(self.k, b"a").hexdigest(), + "bd5d3b5b2b7b57af", + ) + + def test_abc(self): + self.assertSequenceEqual( + MAC(self.k, b"abc").hexdigest(), + "28661e40805b1ff9", + ) + + def test_128U(self): + self.assertSequenceEqual( + MAC(self.k, 128 * b"U").hexdigest(), + "1a06d1bad74580ef", + ) + + def test_13x(self): + self.assertSequenceEqual( + MAC(self.k, 13 * b"x").hexdigest(), + "917ee1f1a668fbd3", + ) + + def test_parts(self): + m = MAC(self.k) + m.update(b"foo") + m.update(b"bar") + self.assertSequenceEqual(m.digest(), MAC(self.k, b"foobar").digest()) + + def test_copy(self): + m = MAC(self.k, b"foo") + c = m.copy() + m.update(b"barbaz") + c.update(b"bar") + c.update(b"baz") + self.assertSequenceEqual(m.digest(), c.digest()) diff --git a/pygost-5.13/pygost/test_gost3410.py b/pygost-5.13/pygost/test_gost3410.py new file mode 100644 index 0000000..cd71535 --- /dev/null +++ b/pygost-5.13/pygost/test_gost3410.py @@ -0,0 +1,495 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from unittest import TestCase + +from pygost.gost3410 import CURVES +from pygost.gost3410 import GOST3410Curve +from pygost.gost3410 import prv_marshal +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import public_key +from pygost.gost3410 import sign +from pygost.gost3410 import uv2xy +from pygost.gost3410 import verify +from pygost.gost3410 import xy2uv +from pygost.utils import bytes2long +from pygost.utils import hexdec +from pygost.utils import hexenc +from pygost.utils import long2bytes + + +class Test341001(TestCase): + def test_rfc(self): + """Test vector from :rfc:`5832` + """ + prv = bytes(bytearray(( + 0x7A, 0x92, 0x9A, 0xDE, 0x78, 0x9B, 0xB9, 0xBE, + 0x10, 0xED, 0x35, 0x9D, 0xD3, 0x9A, 0x72, 0xC1, + 0x1B, 0x60, 0x96, 0x1F, 0x49, 0x39, 0x7E, 0xEE, + 0x1D, 0x19, 0xCE, 0x98, 0x91, 0xEC, 0x3B, 0x28 + ))) + pub_x = bytes(bytearray(( + 0x7F, 0x2B, 0x49, 0xE2, 0x70, 0xDB, 0x6D, 0x90, + 0xD8, 0x59, 0x5B, 0xEC, 0x45, 0x8B, 0x50, 0xC5, + 0x85, 0x85, 0xBA, 0x1D, 0x4E, 0x9B, 0x78, 0x8F, + 0x66, 0x89, 0xDB, 0xD8, 0xE5, 0x6F, 0xD8, 0x0B + ))) + pub_y = bytes(bytearray(( + 0x26, 0xF1, 0xB4, 0x89, 0xD6, 0x70, 0x1D, 0xD1, + 0x85, 0xC8, 0x41, 0x3A, 0x97, 0x7B, 0x3C, 0xBB, + 0xAF, 0x64, 0xD1, 0xC5, 0x93, 0xD2, 0x66, 0x27, + 0xDF, 0xFB, 0x10, 0x1A, 0x87, 0xFF, 0x77, 0xDA + ))) + digest = bytes(bytearray(( + 0x2D, 0xFB, 0xC1, 0xB3, 0x72, 0xD8, 0x9A, 0x11, + 0x88, 0xC0, 0x9C, 0x52, 0xE0, 0xEE, 0xC6, 0x1F, + 0xCE, 0x52, 0x03, 0x2A, 0xB1, 0x02, 0x2E, 0x8E, + 0x67, 0xEC, 0xE6, 0x67, 0x2B, 0x04, 0x3E, 0xE5 + ))) + signature = bytes(bytearray(( + 0x41, 0xAA, 0x28, 0xD2, 0xF1, 0xAB, 0x14, 0x82, + 0x80, 0xCD, 0x9E, 0xD5, 0x6F, 0xED, 0xA4, 0x19, + 0x74, 0x05, 0x35, 0x54, 0xA4, 0x27, 0x67, 0xB8, + 0x3A, 0xD0, 0x43, 0xFD, 0x39, 0xDC, 0x04, 0x93, + 0x01, 0x45, 0x6C, 0x64, 0xBA, 0x46, 0x42, 0xA1, + 0x65, 0x3C, 0x23, 0x5A, 0x98, 0xA6, 0x02, 0x49, + 0xBC, 0xD6, 0xD3, 0xF7, 0x46, 0xB6, 0x31, 0xDF, + 0x92, 0x80, 0x14, 0xF6, 0xC5, 0xBF, 0x9C, 0x40 + ))) + prv = bytes2long(prv) + signature = signature[32:] + signature[:32] + + c = CURVES["id-GostR3410-2001-TestParamSet"] + pubX, pubY = public_key(c, prv) + self.assertSequenceEqual(long2bytes(pubX), pub_x) + self.assertSequenceEqual(long2bytes(pubY), pub_y) + s = sign(c, prv, digest) + self.assertTrue(verify(c, (pubX, pubY), digest, s)) + self.assertTrue(verify(c, (pubX, pubY), digest, signature)) + + def test_sequence(self): + c = CURVES["id-GostR3410-2001-TestParamSet"] + prv = prv_unmarshal(urandom(32)) + pubX, pubY = public_key(c, prv_unmarshal(prv_marshal(c, prv))) + for _ in range(20): + digest = urandom(32) + s = sign(c, prv, digest) + self.assertTrue(verify(c, (pubX, pubY), digest, s)) + + +class Test34102012(TestCase): + def test_1(self): + """Test vector from 34.10-2012 standard itself + """ + curve = CURVES["id-GostR3410-2001-TestParamSet"] + prv = bytes2long(hexdec("7A929ADE789BB9BE10ED359DD39A72C11B60961F49397EEE1D19CE9891EC3B28")) + digest = hexdec("2DFBC1B372D89A1188C09C52E0EEC61FCE52032AB1022E8E67ECE6672B043EE5") + rand = hexdec("77105C9B20BCD3122823C8CF6FCC7B956DE33814E95B7FE64FED924594DCEAB3") + signature = sign(curve, prv, digest, rand) + r = "41aa28d2f1ab148280cd9ed56feda41974053554a42767b83ad043fd39dc0493" + s = "01456c64ba4642a1653c235a98a60249bcd6d3f746b631df928014f6c5bf9c40" + self.assertSequenceEqual(hexenc(signature), s + r) + + def test_2(self): + """Test vector from 34.10-2012 standard itself + """ + curve = GOST3410Curve( + p=3623986102229003635907788753683874306021320925534678605086546150450856166624002482588482022271496854025090823603058735163734263822371964987228582907372403, + q=3623986102229003635907788753683874306021320925534678605086546150450856166623969164898305032863068499961404079437936585455865192212970734808812618120619743, + a=7, + b=1518655069210828534508950034714043154928747527740206436194018823352809982443793732829756914785974674866041605397883677596626326413990136959047435811826396, + x=1928356944067022849399309401243137598997786635459507974357075491307766592685835441065557681003184874819658004903212332884252335830250729527632383493573274, + y=2288728693371972859970012155529478416353562327329506180314497425931102860301572814141997072271708807066593850650334152381857347798885864807605098724013854, + ) + prv = bytes2long(hexdec("0BA6048AADAE241BA40936D47756D7C93091A0E8514669700EE7508E508B102072E8123B2200A0563322DAD2827E2714A2636B7BFD18AADFC62967821FA18DD4")) + digest = hexdec("3754F3CFACC9E0615C4F4A7C4D8DAB531B09B6F9C170C533A71D147035B0C5917184EE536593F4414339976C647C5D5A407ADEDB1D560C4FC6777D2972075B8C") + rand = hexdec("0359E7F4B1410FEACC570456C6801496946312120B39D019D455986E364F365886748ED7A44B3E794434006011842286212273A6D14CF70EA3AF71BB1AE679F1") + signature = sign(curve, prv, digest, rand) + r = "2f86fa60a081091a23dd795e1e3c689ee512a3c82ee0dcc2643c78eea8fcacd35492558486b20f1c9ec197c90699850260c93bcbcd9c5c3317e19344e173ae36" + s = "1081b394696ffe8e6585e7a9362d26b6325f56778aadbc081c0bfbe933d52ff5823ce288e8c4f362526080df7f70ce406a6eeb1f56919cb92a9853bde73e5b4a" + self.assertSequenceEqual(hexenc(signature), s + r) + + def test_gcl3(self): + """Test vector from libgcl3 + """ + p = bytes2long(bytes(bytearray(( + 0x45, 0x31, 0xAC, 0xD1, 0xFE, 0x00, 0x23, 0xC7, + 0x55, 0x0D, 0x26, 0x7B, 0x6B, 0x2F, 0xEE, 0x80, + 0x92, 0x2B, 0x14, 0xB2, 0xFF, 0xB9, 0x0F, 0x04, + 0xD4, 0xEB, 0x7C, 0x09, 0xB5, 0xD2, 0xD1, 0x5D, + 0xF1, 0xD8, 0x52, 0x74, 0x1A, 0xF4, 0x70, 0x4A, + 0x04, 0x58, 0x04, 0x7E, 0x80, 0xE4, 0x54, 0x6D, + 0x35, 0xB8, 0x33, 0x6F, 0xAC, 0x22, 0x4D, 0xD8, + 0x16, 0x64, 0xBB, 0xF5, 0x28, 0xBE, 0x63, 0x73, + )))) + q = bytes2long(bytes(bytearray(( + 0x45, 0x31, 0xAC, 0xD1, 0xFE, 0x00, 0x23, 0xC7, + 0x55, 0x0D, 0x26, 0x7B, 0x6B, 0x2F, 0xEE, 0x80, + 0x92, 0x2B, 0x14, 0xB2, 0xFF, 0xB9, 0x0F, 0x04, + 0xD4, 0xEB, 0x7C, 0x09, 0xB5, 0xD2, 0xD1, 0x5D, + 0xA8, 0x2F, 0x2D, 0x7E, 0xCB, 0x1D, 0xBA, 0xC7, + 0x19, 0x90, 0x5C, 0x5E, 0xEC, 0xC4, 0x23, 0xF1, + 0xD8, 0x6E, 0x25, 0xED, 0xBE, 0x23, 0xC5, 0x95, + 0xD6, 0x44, 0xAA, 0xF1, 0x87, 0xE6, 0xE6, 0xDF, + )))) + a = bytes2long(bytes(bytearray(( + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, + )))) + b = bytes2long(bytes(bytearray(( + 0x1C, 0xFF, 0x08, 0x06, 0xA3, 0x11, 0x16, 0xDA, + 0x29, 0xD8, 0xCF, 0xA5, 0x4E, 0x57, 0xEB, 0x74, + 0x8B, 0xC5, 0xF3, 0x77, 0xE4, 0x94, 0x00, 0xFD, + 0xD7, 0x88, 0xB6, 0x49, 0xEC, 0xA1, 0xAC, 0x43, + 0x61, 0x83, 0x40, 0x13, 0xB2, 0xAD, 0x73, 0x22, + 0x48, 0x0A, 0x89, 0xCA, 0x58, 0xE0, 0xCF, 0x74, + 0xBC, 0x9E, 0x54, 0x0C, 0x2A, 0xDD, 0x68, 0x97, + 0xFA, 0xD0, 0xA3, 0x08, 0x4F, 0x30, 0x2A, 0xDC, + )))) + x = bytes2long(bytes(bytearray(( + 0x24, 0xD1, 0x9C, 0xC6, 0x45, 0x72, 0xEE, 0x30, + 0xF3, 0x96, 0xBF, 0x6E, 0xBB, 0xFD, 0x7A, 0x6C, + 0x52, 0x13, 0xB3, 0xB3, 0xD7, 0x05, 0x7C, 0xC8, + 0x25, 0xF9, 0x10, 0x93, 0xA6, 0x8C, 0xD7, 0x62, + 0xFD, 0x60, 0x61, 0x12, 0x62, 0xCD, 0x83, 0x8D, + 0xC6, 0xB6, 0x0A, 0xA7, 0xEE, 0xE8, 0x04, 0xE2, + 0x8B, 0xC8, 0x49, 0x97, 0x7F, 0xAC, 0x33, 0xB4, + 0xB5, 0x30, 0xF1, 0xB1, 0x20, 0x24, 0x8A, 0x9A, + )))) + y = bytes2long(bytes(bytearray(( + 0x2B, 0xB3, 0x12, 0xA4, 0x3B, 0xD2, 0xCE, 0x6E, + 0x0D, 0x02, 0x06, 0x13, 0xC8, 0x57, 0xAC, 0xDD, + 0xCF, 0xBF, 0x06, 0x1E, 0x91, 0xE5, 0xF2, 0xC3, + 0xF3, 0x24, 0x47, 0xC2, 0x59, 0xF3, 0x9B, 0x2C, + 0x83, 0xAB, 0x15, 0x6D, 0x77, 0xF1, 0x49, 0x6B, + 0xF7, 0xEB, 0x33, 0x51, 0xE1, 0xEE, 0x4E, 0x43, + 0xDC, 0x1A, 0x18, 0xB9, 0x1B, 0x24, 0x64, 0x0B, + 0x6D, 0xBB, 0x92, 0xCB, 0x1A, 0xDD, 0x37, 0x1E, + )))) + prv = bytes(bytearray(( + 0x0B, 0xA6, 0x04, 0x8A, 0xAD, 0xAE, 0x24, 0x1B, + 0xA4, 0x09, 0x36, 0xD4, 0x77, 0x56, 0xD7, 0xC9, + 0x30, 0x91, 0xA0, 0xE8, 0x51, 0x46, 0x69, 0x70, + 0x0E, 0xE7, 0x50, 0x8E, 0x50, 0x8B, 0x10, 0x20, + 0x72, 0xE8, 0x12, 0x3B, 0x22, 0x00, 0xA0, 0x56, + 0x33, 0x22, 0xDA, 0xD2, 0x82, 0x7E, 0x27, 0x14, + 0xA2, 0x63, 0x6B, 0x7B, 0xFD, 0x18, 0xAA, 0xDF, + 0xC6, 0x29, 0x67, 0x82, 0x1F, 0xA1, 0x8D, 0xD4, + ))) + pub_x = bytes(bytearray(( + 0x11, 0x5D, 0xC5, 0xBC, 0x96, 0x76, 0x0C, 0x7B, + 0x48, 0x59, 0x8D, 0x8A, 0xB9, 0xE7, 0x40, 0xD4, + 0xC4, 0xA8, 0x5A, 0x65, 0xBE, 0x33, 0xC1, 0x81, + 0x5B, 0x5C, 0x32, 0x0C, 0x85, 0x46, 0x21, 0xDD, + 0x5A, 0x51, 0x58, 0x56, 0xD1, 0x33, 0x14, 0xAF, + 0x69, 0xBC, 0x5B, 0x92, 0x4C, 0x8B, 0x4D, 0xDF, + 0xF7, 0x5C, 0x45, 0x41, 0x5C, 0x1D, 0x9D, 0xD9, + 0xDD, 0x33, 0x61, 0x2C, 0xD5, 0x30, 0xEF, 0xE1, + ))) + pub_y = bytes(bytearray(( + 0x37, 0xC7, 0xC9, 0x0C, 0xD4, 0x0B, 0x0F, 0x56, + 0x21, 0xDC, 0x3A, 0xC1, 0xB7, 0x51, 0xCF, 0xA0, + 0xE2, 0x63, 0x4F, 0xA0, 0x50, 0x3B, 0x3D, 0x52, + 0x63, 0x9F, 0x5D, 0x7F, 0xB7, 0x2A, 0xFD, 0x61, + 0xEA, 0x19, 0x94, 0x41, 0xD9, 0x43, 0xFF, 0xE7, + 0xF0, 0xC7, 0x0A, 0x27, 0x59, 0xA3, 0xCD, 0xB8, + 0x4C, 0x11, 0x4E, 0x1F, 0x93, 0x39, 0xFD, 0xF2, + 0x7F, 0x35, 0xEC, 0xA9, 0x36, 0x77, 0xBE, 0xEC, + ))) + digest = bytes(bytearray(( + 0x37, 0x54, 0xF3, 0xCF, 0xAC, 0xC9, 0xE0, 0x61, + 0x5C, 0x4F, 0x4A, 0x7C, 0x4D, 0x8D, 0xAB, 0x53, + 0x1B, 0x09, 0xB6, 0xF9, 0xC1, 0x70, 0xC5, 0x33, + 0xA7, 0x1D, 0x14, 0x70, 0x35, 0xB0, 0xC5, 0x91, + 0x71, 0x84, 0xEE, 0x53, 0x65, 0x93, 0xF4, 0x41, + 0x43, 0x39, 0x97, 0x6C, 0x64, 0x7C, 0x5D, 0x5A, + 0x40, 0x7A, 0xDE, 0xDB, 0x1D, 0x56, 0x0C, 0x4F, + 0xC6, 0x77, 0x7D, 0x29, 0x72, 0x07, 0x5B, 0x8C, + ))) + signature = bytes(bytearray(( + 0x2F, 0x86, 0xFA, 0x60, 0xA0, 0x81, 0x09, 0x1A, + 0x23, 0xDD, 0x79, 0x5E, 0x1E, 0x3C, 0x68, 0x9E, + 0xE5, 0x12, 0xA3, 0xC8, 0x2E, 0xE0, 0xDC, 0xC2, + 0x64, 0x3C, 0x78, 0xEE, 0xA8, 0xFC, 0xAC, 0xD3, + 0x54, 0x92, 0x55, 0x84, 0x86, 0xB2, 0x0F, 0x1C, + 0x9E, 0xC1, 0x97, 0xC9, 0x06, 0x99, 0x85, 0x02, + 0x60, 0xC9, 0x3B, 0xCB, 0xCD, 0x9C, 0x5C, 0x33, + 0x17, 0xE1, 0x93, 0x44, 0xE1, 0x73, 0xAE, 0x36, + 0x10, 0x81, 0xB3, 0x94, 0x69, 0x6F, 0xFE, 0x8E, + 0x65, 0x85, 0xE7, 0xA9, 0x36, 0x2D, 0x26, 0xB6, + 0x32, 0x5F, 0x56, 0x77, 0x8A, 0xAD, 0xBC, 0x08, + 0x1C, 0x0B, 0xFB, 0xE9, 0x33, 0xD5, 0x2F, 0xF5, + 0x82, 0x3C, 0xE2, 0x88, 0xE8, 0xC4, 0xF3, 0x62, + 0x52, 0x60, 0x80, 0xDF, 0x7F, 0x70, 0xCE, 0x40, + 0x6A, 0x6E, 0xEB, 0x1F, 0x56, 0x91, 0x9C, 0xB9, + 0x2A, 0x98, 0x53, 0xBD, 0xE7, 0x3E, 0x5B, 0x4A, + ))) + prv = bytes2long(prv) + signature = signature[64:] + signature[:64] + c = GOST3410Curve(p, q, a, b, x, y) + pubX, pubY = public_key(c, prv) + self.assertSequenceEqual(long2bytes(pubX), pub_x) + self.assertSequenceEqual(long2bytes(pubY), pub_y) + s = sign(c, prv, digest) + self.assertTrue(verify(c, (pubX, pubY), digest, s)) + self.assertTrue(verify(c, (pubX, pubY), digest, signature)) + + def test_sequence(self): + c = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + prv = bytes2long(urandom(64)) + pubX, pubY = public_key(c, prv_unmarshal(prv_marshal(c, prv))) + for _ in range(20): + digest = urandom(64) + s = sign(c, prv, digest) + self.assertTrue(verify(c, (pubX, pubY), digest, s)) + self.assertNotIn(b"\x00" * 8, s) + + +class TestUVXYConversion(TestCase): + """Twisted Edwards to Weierstrass coordinates conversion and vice versa + """ + def test_curve1(self): + c = CURVES["id-tc26-gost-3410-2012-256-paramSetA"] + u, v = (0x0D, bytes2long(hexdec("60CA1E32AA475B348488C38FAB07649CE7EF8DBE87F22E81F92B2592DBA300E7"))) + self.assertEqual(uv2xy(c, u, v), (c.x, c.y)) + self.assertEqual(xy2uv(c, c.x, c.y), (u, v)) + + def test_curve2(self): + c = CURVES["id-tc26-gost-3410-2012-512-paramSetC"] + u, v = (0x12, bytes2long(hexdec("469AF79D1FB1F5E16B99592B77A01E2A0FDFB0D01794368D9A56117F7B38669522DD4B650CF789EEBF068C5D139732F0905622C04B2BAAE7600303EE73001A3D"))) + self.assertEqual(uv2xy(c, u, v), (c.x, c.y)) + self.assertEqual(xy2uv(c, c.x, c.y), (u, v)) + + +class Test34102012SESPAKE(TestCase): + """Test vectors for multiplication from :rfc:`8133` + """ + def test_curve1(self): + c = CURVES["id-GostR3410-2001-CryptoPro-A-ParamSet"] + q_ind = ( + 0xA69D51CAF1A309FA9E9B66187759B0174C274E080356F23CFCBFE84D396AD7BB, + 0x5D26F29ECC2E9AC0404DCF7986FA55FE94986362170F54B9616426A659786DAC, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F18127067" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x59495655D1E7C7424C622485F575CCF121F3122D274101E8AB734CC9C9A9B45E, + 0x48D1C311D33C9B701F3B03618562A4A07A044E3AF31E3999E67B487778B53C62, + ) + ) + self.assertEqual( + c.exp(0x1F2538097D5A031FA68BBB43C84D12B3DE47B7061C0D5E24993E0C873CDBA6B3), + ( + 0xBBC77CF42DC1E62D06227935379B4AA4D14FEA4F565DDF4CB4FA4D31579F9676, + 0x8E16604A4AFDF28246684D4996274781F6CB80ABBBA1414C1513EC988509DABF, + ) + ) + self.assertEqual( + c.exp(0xDC497D9EF6324912FD367840EE509A2032AEDB1C0A890D133B45F596FCCBD45D), + ( + 0x6097341C1BE388E83E7CA2DF47FAB86E2271FD942E5B7B2EB2409E49F742BC29, + 0xC81AA48BDB4CA6FA0EF18B9788AE25FE30857AA681B3942217F9FED151BAB7D0, + ), + ) + + def test_curve2(self): + c = CURVES["id-GostR3410-2001-CryptoPro-B-ParamSet"] + q_ind = ( + 0x3D715A874A4B17CB3B517893A9794A2B36C89D2FFC693F01EE4CC27E7F49E399, + 0x1C5A641FCF7CE7E87CDF8CEA38F3DB3096EACE2FAD158384B53953365F4FE7FE, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F18127067" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x6DC2AE26BC691FCA5A73D9C452790D15E34BA5404D92955B914C8D2662ABB985, + 0x3B02AAA9DD65AE30C335CED12F3154BBAC059F66B088306747453EDF6E5DB077, + ) + ) + self.assertEqual( + c.exp(0x499D72B90299CAB0DA1F8BE19D9122F622A13B32B730C46BD0664044F2144FAD), + ( + 0x61D6F916DB717222D74877F179F7EBEF7CD4D24D8C1F523C048E34A1DF30F8DD, + 0x3EC48863049CFCFE662904082E78503F4973A4E105E2F1B18C69A5E7FB209000, + ) + ) + self.assertEqual( + c.exp(0x0F69FF614957EF83668EDC2D7ED614BE76F7B253DB23C5CC9C52BF7DF8F4669D), + ( + 0x33BC6F7E9C0BA10CFB2B72546C327171295508EA97F8C8BA9F890F2478AB4D6C, + 0x75D57B396C396F492F057E9222CCC686437A2AAD464E452EF426FC8EEED1A4A6, + ), + ) + + def test_curve3(self): + c = CURVES["id-GostR3410-2001-CryptoPro-C-ParamSet"] + q_ind = ( + 0x1E36383E43BB6CFA2917167D71B7B5DD3D6D462B43D7C64282AE67DFBEC2559D, + 0x137478A9F721C73932EA06B45CF72E37EB78A63F29A542E563C614650C8B6399, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F18127067" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x945821DAF91E158B839939630655A3B21FF3E146D27041E86C05650EB3B46B59, + 0x3A0C2816AC97421FA0E879605F17F0C9C3EB734CFF196937F6284438D70BDC48, + ) + ) + self.assertEqual( + c.exp(0x3A54AC3F19AD9D0B1EAC8ACDCEA70E581F1DAC33D13FEAFD81E762378639C1A8), + ( + 0x96B7F09C94D297C257A7DA48364C0076E59E48D221CBA604AE111CA3933B446A, + 0x54E4953D86B77ECCEB578500931E822300F7E091F79592CA202A020D762C34A6, + ) + ) + self.assertEqual( + c.exp(0x448781782BF7C0E52A1DD9E6758FD3482D90D3CFCCF42232CF357E59A4D49FD4), + ( + 0x4B9C0AB55A938121F282F48A2CC4396EB16E7E0068B495B0C1DD4667786A3EB7, + 0x223460AA8E09383E9DF9844C5A0F2766484738E5B30128A171B69A77D9509B96, + ), + ) + + def test_curve4(self): + c = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + q_ind = ( + 0x2A17F8833A32795327478871B5C5E88AEFB91126C64B4B8327289BEA62559425D18198F133F400874328B220C74497CD240586CB249E158532CB8090776CD61C, + 0x728F0C4A73B48DA41CE928358FAD26B47A6E094E9362BAE82559F83CDDC4EC3A4676BD3707EDEAF4CD85E99695C64C241EDC622BE87DC0CF87F51F4367F723C5, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F181270671C6213E3930EFDDA26451792C6208122EE60D200520D695DFD9F5F0FD5ABA702" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x0C0AB53D0E0A9C607CAD758F558915A0A7DC5DC87B45E9A58FDDF30EC3385960283E030CD322D9E46B070637785FD49D2CD711F46807A24C40AF9A42C8E2D740, + 0xDF93A8012B86D3A3D4F8A4D487DA15FC739EB31B20B3B0E8C8C032AAF8072C6337CF7D5B404719E5B4407C41D9A3216A08CA69C271484E9ED72B8AAA52E28B8B, + ) + ) + self.assertEqual( + c.exp(0x3CE54325DB52FE798824AEAD11BB16FA766857D04A4AF7D468672F16D90E7396046A46F815693E85B1CE5464DA9270181F82333B0715057BBE8D61D400505F0E), + ( + 0xB93093EB0FCC463239B7DF276E09E592FCFC9B635504EA4531655D76A0A3078E2B4E51CFE2FA400CC5DE9FBE369DB204B3E8ED7EDD85EE5CCA654C1AED70E396, + 0x809770B8D910EA30BD2FA89736E91DC31815D2D9B31128077EEDC371E9F69466F497DC64DD5B1FADC587F860EE256109138C4A9CD96B628E65A8F590520FC882, + ) + ) + self.assertEqual( + c.exp(0xB5C286A79AA8E97EC0E19BC1959A1D15F12F8C97870BA9D68CC12811A56A3BB11440610825796A49D468CDC9C2D02D76598A27973D5960C5F50BCE28D8D345F4), + ( + 0x238B38644E440452A99FA6B93D9FD7DA0CB83C32D3C1E3CFE5DF5C3EB0F9DB91E588DAEDC849EA2FB867AE855A21B4077353C0794716A6480995113D8C20C7AF, + 0xB2273D5734C1897F8D15A7008B862938C8C74CA7E877423D95243EB7EBD02FD2C456CF9FC956F078A59AA86F19DD1075E5167E4ED35208718EA93161C530ED14, + ), + ) + + def test_curve5(self): + c = CURVES["id-tc26-gost-3410-12-512-paramSetB"] + q_ind = ( + 0x7E1FAE8285E035BEC244BEF2D0E5EBF436633CF50E55231DEA9C9CF21D4C8C33DF85D4305DE92971F0A4B4C07E00D87BDBC720EB66E49079285AAF12E0171149, + 0x2CC89998B875D4463805BA0D858A196592DB20AB161558FF2F4EF7A85725D20953967AE621AFDEAE89BB77C83A2528EF6FCE02F68BDA4679D7F2704947DBC408, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F181270671C6213E3930EFDDA26451792C6208122EE60D200520D695DFD9F5F0FD5ABA702" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x7D03E65B8050D1E12CBB601A17B9273B0E728F5021CD47C8A4DD822E4627BA5F9C696286A2CDDA9A065509866B4DEDEDC4A118409604AD549F87A60AFA621161, + 0x16037DAD45421EC50B00D50BDC6AC3B85348BC1D3A2F85DB27C3373580FEF87C2C743B7ED30F22BE22958044E716F93A61CA3213A361A2797A16A3AE62957377, + ) + ) + self.assertEqual( + c.exp(0x715E893FA639BF341296E0623E6D29DADF26B163C278767A7982A989462A3863FE12AEF8BD403D59C4DC4720570D4163DB0805C7C10C4E818F9CB785B04B9997), + ( + 0x10C479EA1C04D3C2C02B0576A9C42D96226FF033C1191436777F66916030D87D02FB93738ED7669D07619FFCE7C1F3C4DB5E5DF49E2186D6FA1E2EB5767602B9, + 0x039F6044191404E707F26D59D979136A831CCE43E1C5F0600D1DDF8F39D0CA3D52FBD943BF04DDCED1AA2CE8F5EBD7487ACDEF239C07D015084D796784F35436, + ) + ) + self.assertEqual( + c.exp(0x30FA8C2B4146C2DBBE82BED04D7378877E8C06753BD0A0FF71EBF2BEFE8DA8F3DC0836468E2CE7C5C961281B6505140F8407413F03C2CB1D201EA1286CE30E6D), + ( + 0x34C0149E7BB91AE377B02573FCC48AF7BFB7B16DEB8F9CE870F384688E3241A3A868588CC0EF4364CCA67D17E3260CD82485C202ADC76F895D5DF673B1788E67, + 0x608E944929BD643569ED5189DB871453F13333A1EAF82B2FE1BE8100E775F13DD9925BD317B63BFAF05024D4A738852332B64501195C1B2EF789E34F23DDAFC5, + ), + ) + + def test_curve6(self): + c = CURVES["id-tc26-gost-3410-2012-256-paramSetA"] + q_ind = ( + 0xB51ADF93A40AB15792164FAD3352F95B66369EB2A4EF5EFAE32829320363350E, + 0x74A358CC08593612F5955D249C96AFB7E8B0BB6D8BD2BBE491046650D822BE18, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F18127067" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0xDBF99827078956812FA48C6E695DF589DEF1D18A2D4D35A96D75BF6854237629, + 0x9FDDD48BFBC57BEE1DA0CFF282884F284D471B388893C48F5ECB02FC18D67589, + ) + ) + self.assertEqual( + c.exp(0x147B72F6684FB8FD1B418A899F7DBECAF5FCE60B13685BAA95328654A7F0707F), + ( + 0x33FBAC14EAE538275A769417829C431BD9FA622B6F02427EF55BD60EE6BC2888, + 0x22F2EBCF960A82E6CDB4042D3DDDA511B2FBA925383C2273D952EA2D406EAE46, + ) + ) + self.assertEqual( + c.exp(0x30D5CFADAA0E31B405E6734C03EC4C5DF0F02F4BA25C9A3B320EE6453567B4CB), + ( + 0x2B2D89FAB735433970564F2F28CFA1B57D640CB902BC6334A538F44155022CB2, + 0x10EF6A82EEF1E70F942AA81D6B4CE5DEC0DDB9447512962874870E6F2849A96F, + ), + ) + + def test_curve7(self): + c = CURVES["id-tc26-gost-3410-2012-512-paramSetC"] + q_ind = ( + 0x489C91784E02E98F19A803ABCA319917F37689E5A18965251CE2FF4E8D8B298F5BA7470F9E0E713487F96F4A8397B3D09A270C9D367EB5E0E6561ADEEB51581D, + 0x684EA885ACA64EAF1B3FEE36C0852A3BE3BD8011B0EF18E203FF87028D6EB5DB2C144A0DCC71276542BFD72CA2A43FA4F4939DA66D9A60793C704A8C94E16F18, + ) + self.assertEqual( + c.exp(bytes2long(hexdec( + "BD04673F7149B18E98155BD1E2724E71D0099AA25174F792D3326C6F181270671C6213E3930EFDDA26451792C6208122EE60D200520D695DFD9F5F0FD5ABA702" + )[::-1]), x=q_ind[0], y=q_ind[1]), + ( + 0x0185AE6271A81BB7F236A955F7CAA26FB63849813C0287D96C83A15AE6B6A86467AB13B6D88CE8CD7DC2E5B97FF5F28FAC2C108F2A3CF3DB5515C9E6D7D210E8, + 0xED0220F92EF771A71C64ECC77986DB7C03D37B3E2AB3E83F32CE5E074A762EC08253C9E2102B87532661275C4B1D16D2789CDABC58ACFDF7318DE70AB64F09B8, + ) + ) + self.assertEqual( + c.exp(0x332F930421D14CFE260042159F18E49FD5A54167E94108AD80B1DE60B13DE7999A34D611E63F3F870E5110247DF8EC7466E648ACF385E52CCB889ABF491EDFF0), + ( + 0x561655966D52952E805574F4281F1ED3A2D498932B00CBA9DECB42837F09835BFFBFE2D84D6B6B242FE7B57F92E1A6F2413E12DDD6383E4437E13D72693469AD, + 0xF6B18328B2715BD7F4178615273A36135BC0BF62F7D8BB9F080164AD36470AD03660F51806C64C6691BADEF30F793720F8E3FEAED631D6A54A4C372DCBF80E82, + ) + ) + self.assertEqual( + c.exp(0x38481771E7D054F96212686B613881880BD8A6C89DDBC656178F014D2C093432A033EE10415F13A160D44C2AD61E6E2E05A7F7EC286BCEA3EA4D4D53F8634FA2), + ( + 0xB7C5818687083433BC1AFF61CB5CA79E38232025E0C1F123B8651E62173CE6873F3E6FFE7281C2E45F4F524F66B0C263616ED08FD210AC4355CA3292B51D71C3, + 0x497F14205DBDC89BDDAF50520ED3B1429AD30777310186BE5E68070F016A44E0C766DB08E8AC23FBDFDE6D675AA4DF591EB18BA0D348DF7AA40973A2F1DCFA55, + ), + ) diff --git a/pygost-5.13/pygost/test_gost3410_vko.py b/pygost-5.13/pygost/test_gost3410_vko.py new file mode 100644 index 0000000..a8e298e --- /dev/null +++ b/pygost-5.13/pygost/test_gost3410_vko.py @@ -0,0 +1,125 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from unittest import TestCase + +from pygost.gost3410 import CURVES +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import pub_unmarshal +from pygost.gost3410 import public_key +from pygost.gost3410_vko import kek_34102001 +from pygost.gost3410_vko import kek_34102012256 +from pygost.gost3410_vko import kek_34102012512 +from pygost.gost3410_vko import ukm_unmarshal +from pygost.utils import bytes2long +from pygost.utils import hexdec + + +class TestVKO34102001(TestCase): + def test_vector(self): + curve = CURVES["id-GostR3410-2001-TestParamSet"] + ukm = ukm_unmarshal(hexdec("5172be25f852a233")) + prv1 = prv_unmarshal(hexdec("1df129e43dab345b68f6a852f4162dc69f36b2f84717d08755cc5c44150bf928")) + prv2 = prv_unmarshal(hexdec("5b9356c6474f913f1e83885ea0edd5df1a43fd9d799d219093241157ac9ed473")) + kek = hexdec("ee4618a0dbb10cb31777b4b86a53d9e7ef6cb3e400101410f0c0f2af46c494a6") + pub1 = public_key(curve, prv1) + pub2 = public_key(curve, prv2) + self.assertSequenceEqual(kek_34102001(curve, prv1, pub2, ukm), kek) + self.assertSequenceEqual(kek_34102001(curve, prv2, pub1, ukm), kek) + + def test_sequence(self): + curve = CURVES["id-GostR3410-2001-TestParamSet"] + for _ in range(10): + ukm = ukm_unmarshal(urandom(8)) + prv1 = bytes2long(urandom(32)) + prv2 = bytes2long(urandom(32)) + pub1 = public_key(curve, prv1) + pub2 = public_key(curve, prv2) + kek1 = kek_34102001(curve, prv1, pub2, ukm) + kek2 = kek_34102001(curve, prv2, pub1, ukm) + self.assertSequenceEqual(kek1, kek2) + kek1 = kek_34102001(curve, prv1, pub1, ukm) + kek2 = kek_34102001(curve, prv2, pub2, ukm) + self.assertNotEqual(kek1, kek2) + + +class TestVKO34102012256(TestCase): + """RFC 7836 + """ + def test_vector(self): + curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + ukm = ukm_unmarshal(hexdec("1d80603c8544c727")) + prvA = prv_unmarshal(hexdec("c990ecd972fce84ec4db022778f50fcac726f46708384b8d458304962d7147f8c2db41cef22c90b102f2968404f9b9be6d47c79692d81826b32b8daca43cb667")) + pubA = pub_unmarshal(hexdec("aab0eda4abff21208d18799fb9a8556654ba783070eba10cb9abb253ec56dcf5d3ccba6192e464e6e5bcb6dea137792f2431f6c897eb1b3c0cc14327b1adc0a7914613a3074e363aedb204d38d3563971bd8758e878c9db11403721b48002d38461f92472d40ea92f9958c0ffa4c93756401b97f89fdbe0b5e46e4a4631cdb5a")) + prvB = prv_unmarshal(hexdec("48c859f7b6f11585887cc05ec6ef1390cfea739b1a18c0d4662293ef63b79e3b8014070b44918590b4b996acfea4edfbbbcccc8c06edd8bf5bda92a51392d0db")) + pubB = pub_unmarshal(hexdec("192fe183b9713a077253c72c8735de2ea42a3dbc66ea317838b65fa32523cd5efca974eda7c863f4954d1147f1f2b25c395fce1c129175e876d132e94ed5a65104883b414c9b592ec4dc84826f07d0b6d9006dda176ce48c391e3f97d102e03bb598bf132a228a45f7201aba08fc524a2d77e43a362ab022ad4028f75bde3b79")) + vko = hexdec("c9a9a77320e2cc559ed72dce6f47e2192ccea95fa648670582c054c0ef36c221") + self.assertSequenceEqual(kek_34102012256(curve, prvA, pubB, ukm), vko) + self.assertSequenceEqual(kek_34102012256(curve, prvB, pubA, ukm), vko) + + def test_sequence(self): + curve = CURVES["id-tc26-gost-3410-2012-256-paramSetA"] + for _ in range(10): + ukm = ukm_unmarshal(urandom(8)) + prv1 = bytes2long(urandom(32)) + prv2 = bytes2long(urandom(32)) + pub1 = public_key(curve, prv1) + pub2 = public_key(curve, prv2) + kek1 = kek_34102012256(curve, prv1, pub2, ukm) + kek2 = kek_34102012256(curve, prv2, pub1, ukm) + self.assertSequenceEqual(kek1, kek2) + kek1 = kek_34102012256(curve, prv1, pub1, ukm) + kek2 = kek_34102012256(curve, prv2, pub2, ukm) + self.assertNotEqual(kek1, kek2) + + def test_pub_is_not_on_curve(self): + with self.assertRaises(ValueError): + kek_34102012256( + CURVES["id-tc26-gost-3410-2012-256-paramSetA"], + bytes2long(urandom(32)), + pub_unmarshal(urandom(64)), + ) + + +class TestVKO34102012512(TestCase): + """RFC 7836 + """ + def test_vector(self): + curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + ukm = ukm_unmarshal(hexdec("1d80603c8544c727")) + prvA = prv_unmarshal(hexdec("c990ecd972fce84ec4db022778f50fcac726f46708384b8d458304962d7147f8c2db41cef22c90b102f2968404f9b9be6d47c79692d81826b32b8daca43cb667")) + pubA = pub_unmarshal(hexdec("aab0eda4abff21208d18799fb9a8556654ba783070eba10cb9abb253ec56dcf5d3ccba6192e464e6e5bcb6dea137792f2431f6c897eb1b3c0cc14327b1adc0a7914613a3074e363aedb204d38d3563971bd8758e878c9db11403721b48002d38461f92472d40ea92f9958c0ffa4c93756401b97f89fdbe0b5e46e4a4631cdb5a")) + prvB = prv_unmarshal(hexdec("48c859f7b6f11585887cc05ec6ef1390cfea739b1a18c0d4662293ef63b79e3b8014070b44918590b4b996acfea4edfbbbcccc8c06edd8bf5bda92a51392d0db")) + pubB = pub_unmarshal(hexdec("192fe183b9713a077253c72c8735de2ea42a3dbc66ea317838b65fa32523cd5efca974eda7c863f4954d1147f1f2b25c395fce1c129175e876d132e94ed5a65104883b414c9b592ec4dc84826f07d0b6d9006dda176ce48c391e3f97d102e03bb598bf132a228a45f7201aba08fc524a2d77e43a362ab022ad4028f75bde3b79")) + vko = hexdec("79f002a96940ce7bde3259a52e015297adaad84597a0d205b50e3e1719f97bfa7ee1d2661fa9979a5aa235b558a7e6d9f88f982dd63fc35a8ec0dd5e242d3bdf") + self.assertSequenceEqual(kek_34102012512(curve, prvA, pubB, ukm), vko) + self.assertSequenceEqual(kek_34102012512(curve, prvB, pubA, ukm), vko) + + def test_sequence(self): + curve = CURVES["id-tc26-gost-3410-12-512-paramSetA"] + for _ in range(10): + ukm = ukm_unmarshal(urandom(8)) + prv1 = bytes2long(urandom(32)) + prv2 = bytes2long(urandom(32)) + pub1 = public_key(curve, prv1) + pub2 = public_key(curve, prv2) + kek1 = kek_34102012512(curve, prv1, pub2, ukm) + kek2 = kek_34102012512(curve, prv2, pub1, ukm) + self.assertSequenceEqual(kek1, kek2) + kek1 = kek_34102012512(curve, prv1, pub1, ukm) + kek2 = kek_34102012512(curve, prv2, pub2, ukm) + self.assertNotEqual(kek1, kek2) diff --git a/pygost-5.13/pygost/test_gost34112012.py b/pygost-5.13/pygost/test_gost34112012.py new file mode 100644 index 0000000..c7c2df9 --- /dev/null +++ b/pygost-5.13/pygost/test_gost34112012.py @@ -0,0 +1,159 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from random import randint +from unittest import skip +from unittest import TestCase +import hmac + +from pygost import gost34112012256 +from pygost import gost34112012512 +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.gost34112012512 import pbkdf2 +from pygost.utils import hexdec +from pygost.utils import hexenc + + +class TestCopy(TestCase): + def runTest(self): + m = GOST34112012256() + c = m.copy() + m.update(b"foobar") + c.update(b"foo") + c.update(b"bar") + self.assertSequenceEqual(m.digest(), c.digest()) + + +class TestSymmetric(TestCase): + def runTest(self): + chunks = [] + for _ in range(randint(1, 10)): + chunks.append(urandom(randint(20, 80))) + m = GOST34112012256() + for chunk in chunks: + m.update(chunk) + self.assertSequenceEqual( + m.hexdigest(), + GOST34112012256(b"".join(chunks)).hexdigest(), + ) + + +class TestHMAC(TestCase): + """RFC 7836 + """ + def test_256(self): + for digestmod in (GOST34112012256, gost34112012256): + self.assertSequenceEqual( + hmac.new( + key=hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + msg=hexdec("0126bdb87800af214341456563780100"), + digestmod=digestmod, + ).hexdigest(), + "a1aa5f7de402d7b3d323f2991c8d4534013137010a83754fd0af6d7cd4922ed9", + ) + + def test_512(self): + for digestmod in (GOST34112012512, gost34112012512): + self.assertSequenceEqual( + hmac.new( + key=hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + msg=hexdec("0126bdb87800af214341456563780100"), + digestmod=digestmod, + ).hexdigest(), + "a59bab22ecae19c65fbde6e5f4e9f5d8549d31f037f9df9b905500e171923a773d5f1530f2ed7e964cb2eedc29e9ad2f3afe93b2814f79f5000ffc0366c251e6", + ) + + +class TestVectors(TestCase): + def test_m1(self): + m = hexdec("323130393837363534333231303938373635343332313039383736353433323130393837363534333231303938373635343332313039383736353433323130")[::-1] + self.assertSequenceEqual( + GOST34112012512(m).digest(), + hexdec("486f64c1917879417fef082b3381a4e211c324f074654c38823a7b76f830ad00fa1fbae42b1285c0352f227524bc9ab16254288dd6863dccd5b9f54a1ad0541b")[::-1] + ) + self.assertSequenceEqual( + GOST34112012256(m).digest(), + hexdec("00557be5e584fd52a449b16b0251d05d27f94ab76cbaa6da890b59d8ef1e159d")[::-1] + ) + + def test_m2(self): + m = u"Се ветри, Стрибожи внуци, веютъ с моря стрелами на храбрыя плъкы Игоревы".encode("cp1251") + self.assertSequenceEqual(m, hexdec("fbe2e5f0eee3c820fbeafaebef20fffbf0e1e0f0f520e0ed20e8ece0ebe5f0f2f120fff0eeec20f120faf2fee5e2202ce8f6f3ede220e8e6eee1e8f0f2d1202ce8f0f2e5e220e5d1")[::-1]) + self.assertSequenceEqual( + GOST34112012512(m).digest(), + hexdec("28fbc9bada033b1460642bdcddb90c3fb3e56c497ccd0f62b8a2ad4935e85f037613966de4ee00531ae60f3b5a47f8dae06915d5f2f194996fcabf2622e6881e")[::-1] + ) + self.assertSequenceEqual( + GOST34112012256(m).digest(), + hexdec("508f7e553c06501d749a66fc28c6cac0b005746d97537fa85d9e40904efed29d")[::-1] + ) + + def test_habr144(self): + """Test vector from https://habr.com/ru/post/452200/ + """ + m = hexdec("d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff0900060000000000000000000000010000000100000000000000001000002400000001000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff") + self.assertSequenceEqual( + GOST34112012256(m).hexdigest(), + "c766085540caaa8953bfcf7a1ba220619cee50d65dc242f82f23ba4b180b18e0", + ) + + +class TestPBKDF2(TestCase): + """http://tc26.ru/.../R_50.1.111-2016.pdf + """ + def test_1(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 1, 64)), + "64770af7f748c3b1c9ac831dbcfd85c26111b30a8a657ddc3056b80ca73e040d2854fd36811f6d825cc4ab66ec0a68a490a9e5cf5156b3a2b7eecddbf9a16b47", + ) + + def test_2(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 2, 64)), + "5a585bafdfbb6e8830d6d68aa3b43ac00d2e4aebce01c9b31c2caed56f0236d4d34b2b8fbd2c4e89d54d46f50e47d45bbac301571743119e8d3c42ba66d348de", + ) + + def test_3(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 4096, 64)), + "e52deb9a2d2aaff4e2ac9d47a41f34c20376591c67807f0477e32549dc341bc7867c09841b6d58e29d0347c996301d55df0d34e47cf68f4e3c2cdaf1d9ab86c3", + ) + + @skip("it takes too long") + def test_4(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 1677216, 64)), + "49e4843bba76e300afe24c4d23dc7392def12f2c0e244172367cd70a8982ac361adb601c7e2a314e8cb7b1e9df840e36ab5615be5d742b6cf203fb55fdc48071", + ) + + def test_5(self): + self.assertSequenceEqual( + hexenc(pbkdf2( + b"passwordPASSWORDpassword", + b"saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + 100, + )), + "b2d8f1245fc4d29274802057e4b54e0a0753aa22fc53760b301cf008679e58fe4bee9addcae99ba2b0b20f431a9c5e50f395c89387d0945aedeca6eb4015dfc2bd2421ee9bb71183ba882ceebfef259f33f9e27dc6178cb89dc37428cf9cc52a2baa2d3a", + ) + + def test_6(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"pass\x00word", b"sa\x00lt", 4096, 64)), + "50df062885b69801a3c10248eb0a27ab6e522ffeb20c991c660f001475d73a4e167f782c18e97e92976d9c1d970831ea78ccb879f67068cdac1910740844e830", + ) diff --git a/pygost-5.13/pygost/test_gost341194.py b/pygost-5.13/pygost/test_gost341194.py new file mode 100644 index 0000000..4ffa6d6 --- /dev/null +++ b/pygost-5.13/pygost/test_gost341194.py @@ -0,0 +1,200 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from unittest import skip +from unittest import TestCase +import hmac + +from pygost import gost341194 +from pygost.gost341194 import GOST341194 +from pygost.gost341194 import pbkdf2 +from pygost.utils import hexenc + + +class TestCopy(TestCase): + def runTest(self): + m = GOST341194() + c = m.copy() + m.update(b"foobar") + c.update(b"foo") + c.update(b"bar") + self.assertSequenceEqual(m.digest(), c.digest()) + + +class TestHMACPEP247(TestCase): + def runTest(self): + h = hmac.new(b"foo", digestmod=gost341194) + h.update(b"foobar") + h.digest() + + +class TestVectors(TestCase): + def test_empty(self): + self.assertSequenceEqual( + GOST341194(b"", "id-GostR3411-94-TestParamSet").hexdigest(), + "ce85b99cc46752fffee35cab9a7b0278abb4c2d2055cff685af4912c49490f8d", + ) + + def test_a(self): + self.assertSequenceEqual( + GOST341194(b"a", "id-GostR3411-94-TestParamSet").hexdigest(), + "d42c539e367c66e9c88a801f6649349c21871b4344c6a573f849fdce62f314dd", + ) + + def test_abc(self): + self.assertSequenceEqual( + GOST341194(b"abc", "id-GostR3411-94-TestParamSet").hexdigest(), + "f3134348c44fb1b2a277729e2285ebb5cb5e0f29c975bc753b70497c06a4d51d", + ) + + def test_message_digest(self): + self.assertSequenceEqual( + GOST341194(b"message digest", "id-GostR3411-94-TestParamSet").hexdigest(), + "ad4434ecb18f2c99b60cbe59ec3d2469582b65273f48de72db2fde16a4889a4d", + ) + + def test_Us(self): + self.assertSequenceEqual( + GOST341194(128 * b"U", "id-GostR3411-94-TestParamSet").hexdigest(), + "53a3a3ed25180cef0c1d85a074273e551c25660a87062a52d926a9e8fe5733a4", + ) + + def test_dog(self): + self.assertSequenceEqual( + GOST341194(b"The quick brown fox jumps over the lazy dog", "id-GostR3411-94-TestParamSet",).hexdigest(), + "77b7fa410c9ac58a25f49bca7d0468c9296529315eaca76bd1a10f376d1f4294", + ) + + def test_cog(self): + self.assertSequenceEqual( + GOST341194(b"The quick brown fox jumps over the lazy cog", "id-GostR3411-94-TestParamSet",).hexdigest(), + "a3ebc4daaab78b0be131dab5737a7f67e602670d543521319150d2e14eeec445", + ) + + def test_rfc32(self): + self.assertSequenceEqual( + GOST341194(b"This is message, length=32 bytes", "id-GostR3411-94-TestParamSet",).hexdigest(), + "b1c466d37519b82e8319819ff32595e047a28cb6f83eff1c6916a815a637fffa", + ) + + def test_rfc50(self): + self.assertSequenceEqual( + GOST341194(b"Suppose the original message has length = 50 bytes", "id-GostR3411-94-TestParamSet",).hexdigest(), + "471aba57a60a770d3a76130635c1fbea4ef14de51f78b4ae57dd893b62f55208", + ) + + +class TestVectorsCryptoPro(TestCase): + """CryptoPro S-box test vectors + """ + def test_empty(self): + self.assertSequenceEqual( + GOST341194(b"", "id-GostR3411-94-CryptoProParamSet").hexdigest(), + "981e5f3ca30c841487830f84fb433e13ac1101569b9c13584ac483234cd656c0", + ) + + def test_a(self): + self.assertSequenceEqual( + GOST341194(b"a", "id-GostR3411-94-CryptoProParamSet").hexdigest(), + "e74c52dd282183bf37af0079c9f78055715a103f17e3133ceff1aacf2f403011", + ) + + def test_abc(self): + self.assertSequenceEqual( + GOST341194(b"abc", "id-GostR3411-94-CryptoProParamSet").hexdigest(), + "b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c", + ) + + def test_message_digest(self): + self.assertSequenceEqual( + GOST341194(b"message digest", "id-GostR3411-94-CryptoProParamSet",).hexdigest(), + "bc6041dd2aa401ebfa6e9886734174febdb4729aa972d60f549ac39b29721ba0", + ) + + def test_dog(self): + self.assertSequenceEqual( + GOST341194(b"The quick brown fox jumps over the lazy dog", "id-GostR3411-94-CryptoProParamSet",).hexdigest(), + "9004294a361a508c586fe53d1f1b02746765e71b765472786e4770d565830a76", + ) + + def test_32(self): + self.assertSequenceEqual( + GOST341194(b"This is message, length=32 bytes", "id-GostR3411-94-CryptoProParamSet",).hexdigest(), + "2cefc2f7b7bdc514e18ea57fa74ff357e7fa17d652c75f69cb1be7893ede48eb", + ) + + def test_50(self): + self.assertSequenceEqual( + GOST341194(b"Suppose the original message has length = 50 bytes", "id-GostR3411-94-CryptoProParamSet",).hexdigest(), + "c3730c5cbccacf915ac292676f21e8bd4ef75331d9405e5f1a61dc3130a65011", + ) + + def test_Us(self): + self.assertSequenceEqual( + GOST341194(128 * b"U", "id-GostR3411-94-CryptoProParamSet").hexdigest(), + "1c4ac7614691bbf427fa2316216be8f10d92edfd37cd1027514c1008f649c4e8", + ) + + +class TestPBKDF2(TestCase): + """http://tc26.ru/methods/containers_v1/Addition_to_PKCS5_v1_0.pdf test vectors + """ + def test_1(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 1, 32)), + "7314e7c04fb2e662c543674253f68bd0b73445d07f241bed872882da21662d58", + ) + + def test_2(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 2, 32)), + "990dfa2bd965639ba48b07b792775df79f2db34fef25f274378872fed7ed1bb3", + ) + + def test_3(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 4096, 32)), + "1f1829a94bdff5be10d0aeb36af498e7a97467f3b31116a5a7c1afff9deadafe", + ) + + @skip("it takes too long") + def test_4(self): + self.assertSequenceEqual( + hexenc(pbkdf2(b"password", b"salt", 16777216, 32)), + "a57ae5a6088396d120850c5c09de0a525100938a59b1b5c3f7810910d05fcd97", + ) + + def test_5(self): + self.assertSequenceEqual( + hexenc(pbkdf2( + b"passwordPASSWORDpassword", + b"saltSALTsaltSALTsaltSALTsaltSALTsalt", + 4096, + 40, + )), + "788358c69cb2dbe251a7bb17d5f4241f265a792a35becde8d56f326b49c85047b7638acb4764b1fd", + ) + + def test_6(self): + self.assertSequenceEqual( + hexenc(pbkdf2( + b"pass\x00word", + b"sa\x00lt", + 4096, + 20, + )), + "43e06c5590b08c0225242373127edf9c8e9c3291", + ) diff --git a/pygost-5.13/pygost/test_gost3412.py b/pygost-5.13/pygost/test_gost3412.py new file mode 100644 index 0000000..5dbb200 --- /dev/null +++ b/pygost-5.13/pygost/test_gost3412.py @@ -0,0 +1,137 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from unittest import TestCase + +from pygost.gost3412 import C +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3412 import L +from pygost.gost3412 import PI +from pygost.utils import hexdec + + +def S(blk): + return bytearray(PI[v] for v in blk) + + +def R(blk): + return L(blk, rounds=1) + + +class STest(TestCase): + def test_vec1(self): + blk = bytearray(hexdec("ffeeddccbbaa99881122334455667700")) + self.assertSequenceEqual(S(blk), hexdec("b66cd8887d38e8d77765aeea0c9a7efc")) + + def test_vec2(self): + blk = bytearray(hexdec("b66cd8887d38e8d77765aeea0c9a7efc")) + self.assertSequenceEqual(S(blk), hexdec("559d8dd7bd06cbfe7e7b262523280d39")) + + def test_vec3(self): + blk = bytearray(hexdec("559d8dd7bd06cbfe7e7b262523280d39")) + self.assertSequenceEqual(S(blk), hexdec("0c3322fed531e4630d80ef5c5a81c50b")) + + def test_vec4(self): + blk = bytearray(hexdec("0c3322fed531e4630d80ef5c5a81c50b")) + self.assertSequenceEqual(S(blk), hexdec("23ae65633f842d29c5df529c13f5acda")) + + +class RTest(TestCase): + def test_vec1(self): + blk = bytearray(hexdec("00000000000000000000000000000100")) + self.assertSequenceEqual(R(blk), hexdec("94000000000000000000000000000001")) + + def test_vec2(self): + blk = bytearray(hexdec("94000000000000000000000000000001")) + self.assertSequenceEqual(R(blk), hexdec("a5940000000000000000000000000000")) + + def test_vec3(self): + blk = bytearray(hexdec("a5940000000000000000000000000000")) + self.assertSequenceEqual(R(blk), hexdec("64a59400000000000000000000000000")) + + def test_vec4(self): + blk = bytearray(hexdec("64a59400000000000000000000000000")) + self.assertSequenceEqual(R(blk), hexdec("0d64a594000000000000000000000000")) + + +class LTest(TestCase): + def test_vec1(self): + blk = bytearray(hexdec("64a59400000000000000000000000000")) + self.assertSequenceEqual(L(blk), hexdec("d456584dd0e3e84cc3166e4b7fa2890d")) + + def test_vec2(self): + blk = bytearray(hexdec("d456584dd0e3e84cc3166e4b7fa2890d")) + self.assertSequenceEqual(L(blk), hexdec("79d26221b87b584cd42fbc4ffea5de9a")) + + def test_vec3(self): + blk = bytearray(hexdec("79d26221b87b584cd42fbc4ffea5de9a")) + self.assertSequenceEqual(L(blk), hexdec("0e93691a0cfc60408b7b68f66b513c13")) + + def test_vec4(self): + blk = bytearray(hexdec("0e93691a0cfc60408b7b68f66b513c13")) + self.assertSequenceEqual(L(blk), hexdec("e6a8094fee0aa204fd97bcb0b44b8580")) + + +class KuznechikTest(TestCase): + key = hexdec("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef") + plaintext = hexdec("1122334455667700ffeeddccbbaa9988") + ciphertext = hexdec("7f679d90bebc24305a468d42b9d4edcd") + + def test_c(self): + self.assertSequenceEqual(C[0], hexdec("6ea276726c487ab85d27bd10dd849401")) + self.assertSequenceEqual(C[1], hexdec("dc87ece4d890f4b3ba4eb92079cbeb02")) + self.assertSequenceEqual(C[2], hexdec("b2259a96b4d88e0be7690430a44f7f03")) + self.assertSequenceEqual(C[3], hexdec("7bcd1b0b73e32ba5b79cb140f2551504")) + self.assertSequenceEqual(C[4], hexdec("156f6d791fab511deabb0c502fd18105")) + self.assertSequenceEqual(C[5], hexdec("a74af7efab73df160dd208608b9efe06")) + self.assertSequenceEqual(C[6], hexdec("c9e8819dc73ba5ae50f5b570561a6a07")) + self.assertSequenceEqual(C[7], hexdec("f6593616e6055689adfba18027aa2a08")) + + def test_roundkeys(self): + ciph = GOST3412Kuznechik(self.key) + self.assertSequenceEqual(ciph.ks[0], hexdec("8899aabbccddeeff0011223344556677")) + self.assertSequenceEqual(ciph.ks[1], hexdec("fedcba98765432100123456789abcdef")) + self.assertSequenceEqual(ciph.ks[2], hexdec("db31485315694343228d6aef8cc78c44")) + self.assertSequenceEqual(ciph.ks[3], hexdec("3d4553d8e9cfec6815ebadc40a9ffd04")) + self.assertSequenceEqual(ciph.ks[4], hexdec("57646468c44a5e28d3e59246f429f1ac")) + self.assertSequenceEqual(ciph.ks[5], hexdec("bd079435165c6432b532e82834da581b")) + self.assertSequenceEqual(ciph.ks[6], hexdec("51e640757e8745de705727265a0098b1")) + self.assertSequenceEqual(ciph.ks[7], hexdec("5a7925017b9fdd3ed72a91a22286f984")) + self.assertSequenceEqual(ciph.ks[8], hexdec("bb44e25378c73123a5f32f73cdb6e517")) + self.assertSequenceEqual(ciph.ks[9], hexdec("72e9dd7416bcf45b755dbaa88e4a4043")) + + def test_encrypt(self): + ciph = GOST3412Kuznechik(self.key) + self.assertSequenceEqual(ciph.encrypt(self.plaintext), self.ciphertext) + + def test_decrypt(self): + ciph = GOST3412Kuznechik(self.key) + self.assertSequenceEqual(ciph.decrypt(self.ciphertext), self.plaintext) + + +class MagmaTest(TestCase): + key = hexdec("ffeeddccbbaa99887766554433221100f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") + plaintext = hexdec("fedcba9876543210") + ciphertext = hexdec("4ee901e5c2d8ca3d") + + def test_encrypt(self): + ciph = GOST3412Magma(self.key) + self.assertSequenceEqual(ciph.encrypt(self.plaintext), self.ciphertext) + + def test_decrypt(self): + ciph = GOST3412Magma(self.key) + self.assertSequenceEqual(ciph.decrypt(self.ciphertext), self.plaintext) diff --git a/pygost-5.13/pygost/test_gost3413.py b/pygost-5.13/pygost/test_gost3413.py new file mode 100644 index 0000000..0098513 --- /dev/null +++ b/pygost-5.13/pygost/test_gost3413.py @@ -0,0 +1,766 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from random import randint +from unittest import TestCase + +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3413 import _mac_ks +from pygost.gost3413 import acpkm +from pygost.gost3413 import acpkm_master +from pygost.gost3413 import cbc_decrypt +from pygost.gost3413 import cbc_encrypt +from pygost.gost3413 import cfb_decrypt +from pygost.gost3413 import cfb_encrypt +from pygost.gost3413 import ctr +from pygost.gost3413 import ctr_acpkm +from pygost.gost3413 import ecb_decrypt +from pygost.gost3413 import ecb_encrypt +from pygost.gost3413 import KEYSIZE +from pygost.gost3413 import mac +from pygost.gost3413 import mac_acpkm_master +from pygost.gost3413 import ofb +from pygost.gost3413 import pad2 +from pygost.gost3413 import pad_iso10126 +from pygost.gost3413 import unpad2 +from pygost.gost3413 import unpad_iso10126 +from pygost.utils import hexdec +from pygost.utils import hexenc +from pygost.utils import strxor + + +class Pad2Test(TestCase): + def test_symmetric(self): + for _ in range(100): + for blocksize in (GOST3412Magma.blocksize, GOST3412Kuznechik.blocksize): + data = urandom(randint(0, blocksize * 3)) + self.assertSequenceEqual( + unpad2(pad2(data, blocksize), blocksize), + data, + ) + + +class GOST3412KuznechikModesTest(TestCase): + key = hexdec("8899aabbccddeeff0011223344556677fedcba98765432100123456789abcdef") + ciph = GOST3412Kuznechik(key) + plaintext = "" + plaintext += "1122334455667700ffeeddccbbaa9988" + plaintext += "00112233445566778899aabbcceeff0a" + plaintext += "112233445566778899aabbcceeff0a00" + plaintext += "2233445566778899aabbcceeff0a0011" + iv = hexdec("1234567890abcef0a1b2c3d4e5f0011223344556677889901213141516171819") + + def test_ecb_vectors(self): + ciphtext = "" + ciphtext += "7f679d90bebc24305a468d42b9d4edcd" + ciphtext += "b429912c6e0032f9285452d76718d08b" + ciphtext += "f0ca33549d247ceef3f5a5313bd4b157" + ciphtext += "d0b09ccde830b9eb3a02c4c5aa8ada98" + self.assertSequenceEqual( + hexenc(ecb_encrypt( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ecb_decrypt( + self.ciph.decrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + )), + self.plaintext, + ) + + def test_ecb_symmetric(self): + for _ in range(100): + pt = pad2(urandom(randint(0, 16 * 2)), GOST3412Kuznechik.blocksize) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = ecb_encrypt(ciph.encrypt, GOST3412Kuznechik.blocksize, pt) + self.assertSequenceEqual(ecb_decrypt( + ciph.decrypt, + GOST3412Kuznechik.blocksize, + ct, + ), pt) + + def test_ctr_vectors(self): + ciphtext = "" + ciphtext += "f195d8bec10ed1dbd57b5fa240bda1b8" + ciphtext += "85eee733f6a13e5df33ce4b33c45dee4" + ciphtext += "a5eae88be6356ed3d5e877f13564a3a5" + ciphtext += "cb91fab1f20cbab6d1c6d15820bdba73" + iv = self.iv[:GOST3412Kuznechik.blocksize // 2] + self.assertSequenceEqual( + hexenc(ctr( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ctr( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + iv, + )), + self.plaintext, + ) + + def test_ctr_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Kuznechik.blocksize // 2) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = ctr(ciph.encrypt, GOST3412Kuznechik.blocksize, pt, iv) + self.assertSequenceEqual(ctr( + ciph.encrypt, + GOST3412Kuznechik.blocksize, + ct, + iv, + ), pt) + + def test_ofb_vectors(self): + ciphtext = "" + ciphtext += "81800a59b1842b24ff1f795e897abd95" + ciphtext += "ed5b47a7048cfab48fb521369d9326bf" + ciphtext += "66a257ac3ca0b8b1c80fe7fc10288a13" + ciphtext += "203ebbc066138660a0292243f6903150" + self.assertSequenceEqual( + hexenc(ofb( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + self.iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ofb( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + self.iv, + )), + self.plaintext, + ) + + def test_ofb_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Kuznechik.blocksize * 2) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = ofb(ciph.encrypt, GOST3412Kuznechik.blocksize, pt, iv) + self.assertSequenceEqual(ofb( + ciph.encrypt, + GOST3412Kuznechik.blocksize, + ct, + iv, + ), pt) + + def test_ofb_manual(self): + iv = [urandom(GOST3412Kuznechik.blocksize) for _ in range(randint(2, 10))] + pt = [ + urandom(GOST3412Kuznechik.blocksize) + for _ in range(len(iv), len(iv) + randint(1, 10)) + ] + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + r = [ciph.encrypt(i) for i in iv] + for i in range(len(pt) - len(iv)): + r.append(ciph.encrypt(r[i])) + ct = [strxor(g, r) for g, r in zip(pt, r)] + self.assertSequenceEqual( + ofb(ciph.encrypt, GOST3412Kuznechik.blocksize, b"".join(pt), b"".join(iv)), + b"".join(ct), + ) + + def test_cbc_vectors(self): + ciphtext = "" + ciphtext += "689972d4a085fa4d90e52e3d6d7dcc27" + ciphtext += "2826e661b478eca6af1e8e448d5ea5ac" + ciphtext += "fe7babf1e91999e85640e8b0f49d90d0" + ciphtext += "167688065a895c631a2d9a1560b63970" + self.assertSequenceEqual( + hexenc(cbc_encrypt( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + self.iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(cbc_decrypt( + self.ciph.decrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + self.iv, + )), + self.plaintext, + ) + + def test_cbc_symmetric(self): + for _ in range(100): + pt = pad2(urandom(randint(0, 16 * 2)), GOST3412Kuznechik.blocksize) + iv = urandom(GOST3412Kuznechik.blocksize * 2) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = cbc_encrypt(ciph.encrypt, GOST3412Kuznechik.blocksize, pt, iv) + self.assertSequenceEqual(cbc_decrypt( + ciph.decrypt, + GOST3412Kuznechik.blocksize, + ct, + iv, + ), pt) + + def test_cfb_vectors(self): + ciphtext = "" + ciphtext += "81800a59b1842b24ff1f795e897abd95" + ciphtext += "ed5b47a7048cfab48fb521369d9326bf" + ciphtext += "79f2a8eb5cc68d38842d264e97a238b5" + ciphtext += "4ffebecd4e922de6c75bd9dd44fbf4d1" + self.assertSequenceEqual( + hexenc(cfb_encrypt( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + self.iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(cfb_decrypt( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(ciphtext), + self.iv, + )), + self.plaintext, + ) + + def test_cfb_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Kuznechik.blocksize * 2) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + ct = cfb_encrypt(ciph.encrypt, GOST3412Kuznechik.blocksize, pt, iv) + self.assertSequenceEqual(cfb_decrypt( + ciph.encrypt, + GOST3412Kuznechik.blocksize, + ct, + iv, + ), pt) + + def test_mac_vectors(self): + k1, k2 = _mac_ks(self.ciph.encrypt, GOST3412Kuznechik.blocksize) + self.assertSequenceEqual(hexenc(k1), "297d82bc4d39e3ca0de0573298151dc7") + self.assertSequenceEqual(hexenc(k2), "52fb05789a73c7941bc0ae65302a3b8e") + self.assertSequenceEqual( + hexenc(mac( + self.ciph.encrypt, + GOST3412Kuznechik.blocksize, + hexdec(self.plaintext), + )[:8]), + "336f4d296059fbe3", + ) + + def test_mac_applies(self): + for _ in range(100): + data = urandom(randint(0, 16 * 2)) + ciph = GOST3412Kuznechik(urandom(KEYSIZE)) + mac(ciph.encrypt, GOST3412Kuznechik.blocksize, data) + + +class GOST3412MagmaModesTest(TestCase): + key = hexdec("ffeeddccbbaa99887766554433221100f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff") + ciph = GOST3412Magma(key) + plaintext = "" + plaintext += "92def06b3c130a59" + plaintext += "db54c704f8189d20" + plaintext += "4a98fb2e67a8024c" + plaintext += "8912409b17b57e41" + iv = hexdec("1234567890abcdef234567890abcdef134567890abcdef12") + + def test_ecb_vectors(self): + ciphtext = "" + ciphtext += "2b073f0494f372a0" + ciphtext += "de70e715d3556e48" + ciphtext += "11d8d9e9eacfbc1e" + ciphtext += "7c68260996c67efb" + self.assertSequenceEqual( + hexenc(ecb_encrypt( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ecb_decrypt( + self.ciph.decrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + )), + self.plaintext, + ) + + def test_ecb_symmetric(self): + for _ in range(100): + pt = pad2(urandom(randint(0, 16 * 2)), 16) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = ecb_encrypt(ciph.encrypt, GOST3412Magma.blocksize, pt) + self.assertSequenceEqual(ecb_decrypt( + ciph.decrypt, + GOST3412Magma.blocksize, + ct, + ), pt) + + def test_ctr_vectors(self): + ciphtext = "" + ciphtext += "4e98110c97b7b93c" + ciphtext += "3e250d93d6e85d69" + ciphtext += "136d868807b2dbef" + ciphtext += "568eb680ab52a12d" + iv = self.iv[:4] + self.assertSequenceEqual( + hexenc(ctr( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ctr( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + iv, + )), + self.plaintext, + ) + + def test_ctr_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Magma.blocksize // 2) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = ctr(ciph.encrypt, GOST3412Magma.blocksize, pt, iv) + self.assertSequenceEqual(ctr( + ciph.encrypt, + GOST3412Magma.blocksize, + ct, + iv, + ), pt) + + def test_ofb_vectors(self): + iv = self.iv[:16] + ciphtext = "" + ciphtext += "db37e0e266903c83" + ciphtext += "0d46644c1f9a089c" + ciphtext += "a0f83062430e327e" + ciphtext += "c824efb8bd4fdb05" + self.assertSequenceEqual( + hexenc(ofb( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(ofb( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + iv, + )), + self.plaintext, + ) + + def test_ofb_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Magma.blocksize * 2) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = ofb(ciph.encrypt, GOST3412Magma.blocksize, pt, iv) + self.assertSequenceEqual(ofb( + ciph.encrypt, + GOST3412Magma.blocksize, + ct, + iv, + ), pt) + + def test_cbc_vectors(self): + ciphtext = "" + ciphtext += "96d1b05eea683919" + ciphtext += "aff76129abb937b9" + ciphtext += "5058b4a1c4bc0019" + ciphtext += "20b78b1a7cd7e667" + self.assertSequenceEqual( + hexenc(cbc_encrypt( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + self.iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(cbc_decrypt( + self.ciph.decrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + self.iv, + )), + self.plaintext, + ) + + def test_cbc_symmetric(self): + for _ in range(100): + pt = pad2(urandom(randint(0, 16 * 2)), 16) + iv = urandom(GOST3412Magma.blocksize * 2) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = cbc_encrypt(ciph.encrypt, GOST3412Magma.blocksize, pt, iv) + self.assertSequenceEqual(cbc_decrypt( + ciph.decrypt, + GOST3412Magma.blocksize, + ct, + iv, + ), pt) + + def test_cfb_vectors(self): + iv = self.iv[:16] + ciphtext = "" + ciphtext += "db37e0e266903c83" + ciphtext += "0d46644c1f9a089c" + ciphtext += "24bdd2035315d38b" + ciphtext += "bcc0321421075505" + self.assertSequenceEqual( + hexenc(cfb_encrypt( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + iv, + )), + ciphtext, + ) + self.assertSequenceEqual( + hexenc(cfb_decrypt( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(ciphtext), + iv, + )), + self.plaintext, + ) + + def test_cfb_symmetric(self): + for _ in range(100): + pt = urandom(randint(0, 16 * 2)) + iv = urandom(GOST3412Magma.blocksize * 2) + ciph = GOST3412Magma(urandom(KEYSIZE)) + ct = cfb_encrypt(ciph.encrypt, GOST3412Magma.blocksize, pt, iv) + self.assertSequenceEqual(cfb_decrypt( + ciph.encrypt, + GOST3412Magma.blocksize, + ct, + iv, + ), pt) + + def test_mac_vectors(self): + k1, k2 = _mac_ks(self.ciph.encrypt, GOST3412Magma.blocksize) + self.assertSequenceEqual(hexenc(k1), "5f459b3342521424") + self.assertSequenceEqual(hexenc(k2), "be8b366684a42848") + self.assertSequenceEqual( + hexenc(mac( + self.ciph.encrypt, + GOST3412Magma.blocksize, + hexdec(self.plaintext), + )[:4]), + "154e7210", + ) + + def test_mac_applies(self): + for _ in range(100): + data = urandom(randint(0, 16 * 2)) + ciph = GOST3412Magma(urandom(KEYSIZE)) + mac(ciph.encrypt, GOST3412Magma.blocksize, data) + + +class TestVectorACPKM(TestCase): + """Test vectors from Р 1323565.1.017-2018 + """ + key = hexdec("8899AABBCCDDEEFF0011223344556677FEDCBA98765432100123456789ABCDEF") + + def test_magma_ctr_acpkm(self): + key = acpkm(GOST3412Magma(self.key).encrypt, GOST3412Magma.blocksize) + self.assertSequenceEqual(key, hexdec("863EA017842C3D372B18A85A28E2317D74BEFC107720DE0C9E8AB974ABD00CA0")) + key = acpkm(GOST3412Magma(key).encrypt, GOST3412Magma.blocksize) + self.assertSequenceEqual(key, hexdec("49A5E2677DE555982B8AD5E826652D17EEC847BF5B3997A81CF7FE7F1187BD27")) + key = acpkm(GOST3412Magma(key).encrypt, GOST3412Magma.blocksize) + self.assertSequenceEqual(key, hexdec("3256BF3F97B5667426A9FB1C5EAABE41893CCDD5A868F9B63B0AA90720FA43C4")) + + def test_magma_ctr(self): + encrypter = GOST3412Magma(self.key).encrypt + plaintext = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A +11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 +22 33 44 55 66 77 88 99 + """.replace("\n", "").replace(" ", "")) + iv = hexdec("12345678") + ciphertext = hexdec(""" +2A B8 1D EE EB 1E 4C AB 68 E1 04 C4 BD 6B 94 EA +C7 2C 67 AF 6C 2E 5B 6B 0E AF B6 17 70 F1 B3 2E +A1 AE 71 14 9E ED 13 82 AB D4 67 18 06 72 EC 6F +84 A2 F1 5B 3F CA 72 C1 + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + ctr_acpkm( + GOST3412Magma, + encrypter, + bs=GOST3412Magma.blocksize, + section_size=GOST3412Magma.blocksize * 2, + data=plaintext, + iv=iv + ), + ciphertext, + ) + self.assertSequenceEqual( + ctr_acpkm( + GOST3412Magma, + encrypter, + bs=GOST3412Magma.blocksize, + section_size=GOST3412Magma.blocksize * 2, + data=ciphertext, + iv=iv + ), + plaintext, + ) + + def test_kuznechik_ctr_acpkm(self): + key = acpkm(GOST3412Kuznechik(self.key).encrypt, GOST3412Kuznechik.blocksize) + self.assertSequenceEqual(key, hexdec("2666ED40AE687811745CA0B448F57A7B390ADB5780307E8E9659AC403AE60C60")) + key = acpkm(GOST3412Kuznechik(key).encrypt, GOST3412Kuznechik.blocksize) + self.assertSequenceEqual(key, hexdec("BB3DD5402E999B7A3DEBB0DB45448EC530F07365DFEE3ABA8415F77AC8F34CE8")) + key = acpkm(GOST3412Kuznechik(key).encrypt, GOST3412Kuznechik.blocksize) + self.assertSequenceEqual(key, hexdec("23362FD553CAD2178299A5B5A2D4722E3BB83C730A8BF57CE2DD004017F8C565")) + + def test_kuznechik_ctr(self): + encrypter = GOST3412Kuznechik(self.key).encrypt + iv = hexdec("1234567890ABCEF0") + plaintext = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A +11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 +22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 +33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 +44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 33 +55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 33 44 + """.replace("\n", "").replace(" ", "")) + ciphertext = hexdec(""" +F1 95 D8 BE C1 0E D1 DB D5 7B 5F A2 40 BD A1 B8 +85 EE E7 33 F6 A1 3E 5D F3 3C E4 B3 3C 45 DE E4 +4B CE EB 8F 64 6F 4C 55 00 17 06 27 5E 85 E8 00 +58 7C 4D F5 68 D0 94 39 3E 48 34 AF D0 80 50 46 +CF 30 F5 76 86 AE EC E1 1C FC 6C 31 6B 8A 89 6E +DF FD 07 EC 81 36 36 46 0C 4F 3B 74 34 23 16 3E +64 09 A9 C2 82 FA C8 D4 69 D2 21 E7 FB D6 DE 5D + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + ctr_acpkm( + GOST3412Kuznechik, + encrypter, + bs=GOST3412Kuznechik.blocksize, + section_size=GOST3412Kuznechik.blocksize * 2, + data=plaintext, + iv=iv, + ), + ciphertext, + ) + self.assertSequenceEqual( + ctr_acpkm( + GOST3412Kuznechik, + encrypter, + bs=GOST3412Kuznechik.blocksize, + section_size=GOST3412Kuznechik.blocksize * 2, + data=ciphertext, + iv=iv, + ), + plaintext, + ) + + def test_magma_omac_1_5_blocks(self): + encrypter = GOST3412Magma(self.key).encrypt + key_section_size = 640 // 8 + self.assertSequenceEqual( + acpkm_master( + GOST3412Magma, + encrypter, + key_section_size=key_section_size, + bs=GOST3412Magma.blocksize, + keymat_len=KEYSIZE + GOST3412Magma.blocksize, + ), + hexdec("0DF2F5273DA328932AC49D81D36B2558A50DBF9BBCAC74A614B2CCB2F1CBCD8A70638E3DE8B3571E"), + ) + text = hexdec("1122334455667700FFEEDDCC") + self.assertSequenceEqual( + mac_acpkm_master( + GOST3412Magma, + encrypter, + key_section_size, + section_size=GOST3412Magma.blocksize * 2, + bs=GOST3412Magma.blocksize, + data=text, + ), + hexdec("A0540E3730ACBCF3"), + ) + + def test_magma_omac_5_blocks(self): + encrypter = GOST3412Magma(self.key).encrypt + key_section_size = 640 // 8 + self.assertSequenceEqual( + acpkm_master( + GOST3412Magma, + encrypter, + key_section_size=key_section_size, + bs=GOST3412Magma.blocksize, + keymat_len=3 * (KEYSIZE + GOST3412Magma.blocksize), + ), + hexdec(""" +0D F2 F5 27 3D A3 28 93 2A C4 9D 81 D3 6B 25 58 +A5 0D BF 9B BC AC 74 A6 14 B2 CC B2 F1 CB CD 8A +70 63 8E 3D E8 B3 57 1E 8D 38 26 D5 5E 63 A1 67 +E2 40 66 40 54 7B 9F 1F 5F 2B 43 61 2A AE AF DA +18 0B AC 86 04 DF A6 FE 53 C2 CE 27 0E 9C 9F 52 +68 D0 FD BF E1 A3 BD D9 BE 5B 96 D0 A1 20 23 48 +6E F1 71 0F 92 4A E0 31 30 52 CB 5F CA 0B 79 1E +1B AB E8 57 6D 0F E3 A8 + """.replace("\n", "").replace(" ", "")), + ) + text = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A +11 22 33 44 55 66 77 88 + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + mac_acpkm_master( + GOST3412Magma, + encrypter, + key_section_size, + section_size=GOST3412Magma.blocksize * 2, + bs=GOST3412Magma.blocksize, + data=text, + ), + hexdec("34008DAD5496BB8E"), + ) + + def test_kuznechik_omac_1_5_blocks(self): + encrypter = GOST3412Kuznechik(self.key).encrypt + key_section_size = 768 // 8 + self.assertSequenceEqual( + acpkm_master( + GOST3412Kuznechik, + encrypter, + key_section_size=key_section_size, + bs=GOST3412Kuznechik.blocksize, + keymat_len=KEYSIZE + GOST3412Kuznechik.blocksize, + ), + hexdec(""" +0C AB F1 F2 EF BC 4A C1 60 48 DF 1A 24 C6 05 B2 +C0 D1 67 3D 75 86 A8 EC 0D D4 2C 45 A4 F9 5B AE +0F 2E 26 17 E4 71 48 68 0F C3 E6 17 8D F2 C1 37 + """.replace("\n", "").replace(" ", "")) + ) + text = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + mac_acpkm_master( + GOST3412Kuznechik, + encrypter, + key_section_size, + section_size=GOST3412Kuznechik.blocksize * 2, + bs=GOST3412Kuznechik.blocksize, + data=text, + ), + hexdec("B5367F47B62B995EEB2A648C5843145E"), + ) + + def test_kuznechik_omac_5_blocks(self): + encrypter = GOST3412Kuznechik(self.key).encrypt + key_section_size = 768 // 8 + self.assertSequenceEqual( + acpkm_master( + GOST3412Kuznechik, + encrypter, + key_section_size=key_section_size, + bs=GOST3412Kuznechik.blocksize, + keymat_len=3 * (KEYSIZE + GOST3412Kuznechik.blocksize), + ), + hexdec(""" +0C AB F1 F2 EF BC 4A C1 60 48 DF 1A 24 C6 05 B2 +C0 D1 67 3D 75 86 A8 EC 0D D4 2C 45 A4 F9 5B AE +0F 2E 26 17 E4 71 48 68 0F C3 E6 17 8D F2 C1 37 +C9 DD A8 9C FF A4 91 FE AD D9 B3 EA B7 03 BB 31 +BC 7E 92 7F 04 94 72 9F 51 B4 9D 3D F9 C9 46 08 +00 FB BC F5 ED EE 61 0E A0 2F 01 09 3C 7B C7 42 +D7 D6 27 15 01 B1 77 77 52 63 C2 A3 49 5A 83 18 +A8 1C 79 A0 4F 29 66 0E A3 FD A8 74 C6 30 79 9E +14 2C 57 79 14 FE A9 0D 3B C2 50 2E 83 36 85 D9 + """.replace("\n", "").replace(" ", "")), + ) + text = hexdec(""" +11 22 33 44 55 66 77 00 FF EE DD CC BB AA 99 88 +00 11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A +11 22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 +22 33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 +33 44 55 66 77 88 99 AA BB CC EE FF 0A 00 11 22 + """.replace("\n", "").replace(" ", "")) + self.assertSequenceEqual( + mac_acpkm_master( + GOST3412Kuznechik, + encrypter, + key_section_size, + section_size=GOST3412Kuznechik.blocksize * 2, + bs=GOST3412Kuznechik.blocksize, + data=text, + ), + hexdec("FBB8DCEE45BEA67C35F58C5700898E5D"), + ) + + +class ISO10126Test(TestCase): + def test_symmetric(self): + for _ in range(100): + for blocksize in (GOST3412Magma.blocksize, GOST3412Kuznechik.blocksize): + data = urandom(randint(0, blocksize * 3)) + padded = pad_iso10126(data, blocksize) + self.assertSequenceEqual(unpad_iso10126(padded, blocksize), data) + with self.assertRaises(ValueError): + unpad_iso10126(padded[1:], blocksize) + + def test_small(self): + with self.assertRaises(ValueError): + unpad_iso10126(b"foobar\x00\x09", 8) diff --git a/pygost-5.13/pygost/test_kdf.py b/pygost-5.13/pygost/test_kdf.py new file mode 100644 index 0000000..6921cfc --- /dev/null +++ b/pygost-5.13/pygost/test_kdf.py @@ -0,0 +1,58 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from unittest import TestCase + +from pygost.kdf import kdf_gostr3411_2012_256 +from pygost.kdf import kdf_tree_gostr3411_2012_256 +from pygost.utils import hexdec + + +class TestKDFGOSTR34112012256(TestCase): + def runTest(self): + self.assertEqual( + kdf_gostr3411_2012_256( + hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + hexdec("26bdb878"), + hexdec("af21434145656378"), + ), + hexdec("a1aa5f7de402d7b3d323f2991c8d4534013137010a83754fd0af6d7cd4922ed9"), + ) + + +class TestKDFTREEGOSTR34112012256(TestCase): + def runTest(self): + self.assertSequenceEqual( + kdf_tree_gostr3411_2012_256( + hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + hexdec("26bdb878"), + hexdec("af21434145656378"), + 1, + ), + (hexdec("a1aa5f7de402d7b3d323f2991c8d4534013137010a83754fd0af6d7cd4922ed9"),), + ) + self.assertSequenceEqual( + kdf_tree_gostr3411_2012_256( + hexdec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"), + hexdec("26bdb878"), + hexdec("af21434145656378"), + 2, + ), + ( + hexdec("22b6837845c6bef65ea71672b265831086d3c76aebe6dae91cad51d83f79d16b"), + hexdec("074c9330599d7f8d712fca54392f4ddde93751206b3584c8f43f9e6dc51531f9"), + ), + ) diff --git a/pygost-5.13/pygost/test_mgm.py b/pygost-5.13/pygost/test_mgm.py new file mode 100644 index 0000000..e70a7f5 --- /dev/null +++ b/pygost-5.13/pygost/test_mgm.py @@ -0,0 +1,75 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from random import randint +from unittest import TestCase + +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3412 import KEYSIZE +from pygost.mgm import MGM +from pygost.mgm import nonce_prepare +from pygost.utils import hexdec + + +class TestVector(TestCase): + def runTest(self): + key = hexdec("8899AABBCCDDEEFF0011223344556677FEDCBA98765432100123456789ABCDEF") + ad = hexdec("0202020202020202010101010101010104040404040404040303030303030303EA0505050505050505") + plaintext = hexdec("1122334455667700FFEEDDCCBBAA998800112233445566778899AABBCCEEFF0A112233445566778899AABBCCEEFF0A002233445566778899AABBCCEEFF0A0011AABBCC") + mgm = MGM(GOST3412Kuznechik(key).encrypt, GOST3412Kuznechik.blocksize) + ciphertext = mgm.seal(plaintext[:16], plaintext, ad) + self.assertSequenceEqual(ciphertext[:len(plaintext)], hexdec("A9757B8147956E9055B8A33DE89F42FC8075D2212BF9FD5BD3F7069AADC16B39497AB15915A6BA85936B5D0EA9F6851CC60C14D4D3F883D0AB94420695C76DEB2C7552")) + self.assertSequenceEqual(ciphertext[len(plaintext):], hexdec("CF5D656F40C34F5C46E8BB0E29FCDB4C")) + self.assertSequenceEqual(mgm.open(plaintext[:16], ciphertext, ad), plaintext) + + +class TestSymmetric(TestCase): + def _itself(self, mgm, bs, tag_size): + for _ in range(1000): + nonce = nonce_prepare(urandom(bs)) + ad = urandom(randint(0, 20)) + pt = urandom(randint(0, 20)) + if len(ad) + len(pt) == 0: + continue + ct = mgm.seal(nonce, pt, ad) + self.assertEqual(len(ct) - tag_size, len(pt)) + self.assertSequenceEqual(mgm.open(nonce, ct, ad), pt) + + def test_magma(self): + for tag_size in ( + GOST3412Magma.blocksize, + GOST3412Magma.blocksize - 2, + ): + mgm = MGM( + GOST3412Magma(urandom(KEYSIZE)).encrypt, + GOST3412Magma.blocksize, + tag_size, + ) + self._itself(mgm, GOST3412Magma.blocksize, tag_size) + + def test_kuznechik(self): + for tag_size in ( + GOST3412Kuznechik.blocksize, + GOST3412Kuznechik.blocksize - 2, + ): + mgm = MGM( + GOST3412Kuznechik(urandom(KEYSIZE)).encrypt, + GOST3412Kuznechik.blocksize, + tag_size, + ) + self._itself(mgm, GOST3412Kuznechik.blocksize, tag_size) diff --git a/pygost-5.13/pygost/test_pfx.py b/pygost-5.13/pygost/test_pfx.py new file mode 100644 index 0000000..a2760bf --- /dev/null +++ b/pygost-5.13/pygost/test_pfx.py @@ -0,0 +1,680 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from base64 import b64decode +from hmac import new as hmac_new +from unittest import skipIf +from unittest import TestCase + +from pygost import gost3410 +from pygost.gost28147 import cfb_decrypt +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.gost34112012512 import pbkdf2 as gost34112012_pbkdf2 +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.gost3412 import KEYSIZE +from pygost.gost3413 import ctr_acpkm +from pygost.gost3413 import mac as omac +from pygost.kdf import kdf_tree_gostr3411_2012_256 +from pygost.kdf import keg +from pygost.utils import hexdec +from pygost.wrap import kimp15 + + +try: + from pyderasn import OctetString + + from pygost.asn1schemas.cms import EncryptedData + from pygost.asn1schemas.cms import EnvelopedData + from pygost.asn1schemas.cms import SignedAttributes + from pygost.asn1schemas.cms import SignedData + from pygost.asn1schemas.oids import id_data + from pygost.asn1schemas.oids import id_envelopedData + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_ctracpkm + from pygost.asn1schemas.oids import id_gostr3412_2015_kuznyechik_wrap_kexp15 + from pygost.asn1schemas.oids import id_messageDigest + from pygost.asn1schemas.oids import id_pbes2 + from pygost.asn1schemas.oids import id_pkcs12_bagtypes_certBag + from pygost.asn1schemas.oids import id_pkcs12_bagtypes_keyBag + from pygost.asn1schemas.oids import id_pkcs12_bagtypes_pkcs8ShroudedKeyBag + from pygost.asn1schemas.oids import id_pkcs9_certTypes_x509Certificate + from pygost.asn1schemas.oids import id_signedData + from pygost.asn1schemas.oids import id_tc26_agreement_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256 + from pygost.asn1schemas.pfx import CertBag + from pygost.asn1schemas.pfx import KeyBag + from pygost.asn1schemas.pfx import OctetStringSafeContents + from pygost.asn1schemas.pfx import PBES2Params + from pygost.asn1schemas.pfx import PFX + from pygost.asn1schemas.pfx import PKCS8ShroudedKeyBag + from pygost.asn1schemas.pfx import SafeContents + from pygost.asn1schemas.x509 import Certificate +except ImportError: + pyderasn_exists = False +else: + pyderasn_exists = True + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestPFX(TestCase): + """PFX test vectors from "Транспортный ключевой контейнер" (R50.1.112-2016.pdf) + """ + pfx_raw = b64decode(""" +MIIFqgIBAzCCBSsGCSqGSIb3DQEHAaCCBRwEggUYMIIFFDCCASIGCSqGSIb3DQEH +AaCCARMEggEPMIIBCzCCAQcGCyqGSIb3DQEMCgECoIHgMIHdMHEGCSqGSIb3DQEF +DTBkMEEGCSqGSIb3DQEFDDA0BCD5qZr0TTIsBvdgUoq/zFwOzdyJohj6/4Wiyccg +j9AK/QICB9AwDAYIKoUDBwEBBAIFADAfBgYqhQMCAhUwFQQI3Ip/Vp0IsyIGCSqF +AwcBAgUBAQRoSfLhgx9s/zn+BjnhT0ror07vS55Ys5hgvVpWDx4mXGWWyez/2sMc +aFgSr4H4UTGGwoMynGLpF1IOVo+bGJ0ePqHB+gS5OL9oV+PUmZ/ELrRENKlCDqfY +WvpSystX29CvCFrnTnDsbBYxFTATBgkqhkiG9w0BCRUxBgQEAQAAADCCA+oGCSqG +SIb3DQEHBqCCA9swggPXAgEAMIID0AYJKoZIhvcNAQcBMHEGCSqGSIb3DQEFDTBk +MEEGCSqGSIb3DQEFDDA0BCCJTJLZQRi1WIpQHzyjXbq7+Vw2+1280C45x8ff6kMS +VAICB9AwDAYIKoUDBwEBBAIFADAfBgYqhQMCAhUwFQQIxepowwvS11MGCSqFAwcB +AgUBAYCCA06n09P/o+eDEKoSWpvlpOLKs7dKmVquKzJ81nCngvLQ5fEWL1WkxwiI +rEhm53JKLD0wy4hekalEk011Bvc51XP9gkDkmaoBpnV/TyKIY35wl6ATfeGXno1M +KoA+Ktdhv4gLnz0k2SXdkUj11JwYskXue+REA0p4m2ZsoaTmvoODamh9JeY/5Qjy +Xe58CGnyXFzX3eU86qs4WfdWdS3NzYYOk9zzVl46le9u79O/LnW2j4n2of/Jpk/L +YjrRmz5oYeQOqKOKhEyhpO6e+ejr6laduEv7TwJQKRNiygogbVvkNn3VjHTSOUG4 +W+3NRPhjb0jD9obdyx6MWa6O3B9bUzFMNav8/gYn0vTDxqXMLy/92oTngNrVx6Gc +cNl128ISrDS6+RxtAMiEBRK6xNkemqX5yNXG5GrLQQFGP6mbs2nNpjKlgj3pljmX +Eky2/G78XiJrv02OgGs6CKnI9nMpa6N7PBHV34MJ6EZzWOWDRQ420xk63mnicrs0 +WDVJ0xjdu4FW3iEk02EaiRTvGBpa6GL7LBp6QlaXSSwONx725cyRsL9cTlukqXER +WHDlMpjYLbkGZRrCc1myWgEfsputfSIPNF/oLv9kJNWacP3uuDOfecg3us7eg2OA +xo5zrYfn39GcBMF1WHAYRO/+PnJb9jrDuLAE8+ONNqjNulWNK9CStEhb6Te+yE6q +oeP6hJjFLi+nFLE9ymIo0A7gLQD5vzFvl+7v1ZNVnQkwRUsWoRiEVVGnv3Z1iZU6 +xStxgoHMl62V/P5cz4dr9vJM2adEWNZcVXl6mk1H8DRc1sRGnvs2l237oKWRVntJ +hoWnZ8qtD+3ZUqsX79QhVzUQBzKuBt6jwNhaHLGl5B+Or/zA9FezsOh6+Uc+fZaV +W7fFfeUyWwGy90XD3ybTrjzep9f3nt55Z2c+fu2iEwhoyImWLuC3+CVhf9Af59j9 +8/BophMJuATDJEtgi8rt4vLnfxKu250Mv2ZpbfF69EGTgFYbwc55zRfaUG9zlyCu +1YwMJ6HC9FUVtJp9gObSrirbzTH7mVaMjQkBLotazWbegzI+be8V3yT06C+ehD+2 +GdLWAVs9hp8gPHEUShb/XrgPpDSJmFlOiyeOFBO/j4edDACKqVcwdjBOMAoGCCqF +AwcBAQIDBEAIFX0fyZe20QKKhWm6WYX+S92Gt6zaXroXOvAmayzLfZ5Sd9C2t9zZ +JSg6M8RBUYpw/8ym5ou1o2nDa09M5zF3BCCpzyCQBI+rzfISeKvPV1ROfcXiYU93 +mwcl1xQV2G5/fgICB9A= + """) + password = u"Пароль для PFX" + + def test_shrouded_key_bag(self): + private_key_info_expected = b64decode(b""" +MGYCAQAwHwYIKoUDBwEBAQEwEwYHKoUDAgIjAQYIKoUDBwEBAgIEQEYbRu86z+1JFKDcPDN9UbTG +G2ki9enTqos4KpUU0j9IDpl1UXiaA1YDIwUjlAp+81GkLmyt8Fw6Gt/X5JZySAY= + """) + + pfx, tail = PFX().decode(self.pfx_raw) + self.assertSequenceEqual(tail, b"") + _, outer_safe_contents = pfx["authSafe"]["content"].defined + safe_contents, tail = OctetStringSafeContents().decode( + bytes(outer_safe_contents[0]["bagValue"]), + ) + self.assertSequenceEqual(tail, b"") + safe_bag = safe_contents[0] + shrouded_key_bag, tail = PKCS8ShroudedKeyBag().decode( + bytes(safe_bag["bagValue"]), + ) + self.assertSequenceEqual(tail, b"") + _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + + key = gost34112012_pbkdf2( + password=self.password.encode("utf-8"), + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("309dd0354c5603739403f2335e9e2055138f8b5c98b63009de0635eea1fd7ba8") + self.assertSequenceEqual( + cfb_decrypt( + key, + bytes(shrouded_key_bag["encryptedData"]), + iv=bytes(enc_scheme_params["iv"]), + sbox="id-tc26-gost-28147-param-Z", + ), + private_key_info_expected, + ) + + def test_encrypted_data(self): + cert_bag_expected = b64decode(b""" +MIIDSjCCA0YGCyqGSIb3DQEMCgEDoIIDHjCCAxoGCiqGSIb3DQEJFgGgggMKBIIDBjCCAwIwggKt +oAMCAQICEAHQaF8xH5bAAAAACycJAAEwDAYIKoUDBwEBAwIFADBgMQswCQYDVQQGEwJSVTEVMBMG +A1UEBwwM0JzQvtGB0LrQstCwMQ8wDQYDVQQKDAbQotCaMjYxKTAnBgNVBAMMIENBIGNlcnRpZmlj +YXRlIChQS0NTIzEyIGV4YW1wbGUpMB4XDTE1MDMyNzA3MjUwMFoXDTIwMDMyNzA3MjMwMFowZDEL +MAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MS0wKwYD +VQQDDCRUZXN0IGNlcnRpZmljYXRlIDEgKFBLQ1MjMTIgZXhhbXBsZSkwZjAfBggqhQMHAQEBATAT +BgcqhQMCAiMBBggqhQMHAQECAgNDAARA1xzymkpvr2dYJT8WTOX3Dt96/+hGsXNytUQpkWB5ImJM +4tg9AsC4RIUwV5H41MhG0uBRFweTzN6AsAdBvhTClYEJADI3MDkwMDAxo4IBKTCCASUwKwYDVR0Q +BCQwIoAPMjAxNTAzMjcwNzI1MDBagQ8yMDE2MDMyNzA3MjUwMFowDgYDVR0PAQH/BAQDAgTwMB0G +A1UdDgQWBBQhWOsRQ68yYN2Utg/owHoWcqsVbTAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUH +AwQwDAYDVR0TAQH/BAIwADCBmQYDVR0jBIGRMIGOgBQmnc7Xh5ykb5t/BMwOkxA4drfEmqFkpGIw +YDELMAkGA1UEBhMCUlUxFTATBgNVBAcMDNCc0L7RgdC60LLQsDEPMA0GA1UECgwG0KLQmjI2MSkw +JwYDVQQDDCBDQSBjZXJ0aWZpY2F0ZSAoUEtDUyMxMiBleGFtcGxlKYIQAdBoXvL8TSAAAAALJwkA +ATAMBggqhQMHAQEDAgUAA0EA9oq0Vvk8kkgIwkp0x0J5eKtia4MNTiwKAm7jgnCZIx3O98BThaTX +3ZQhEo2RL9pTCPr6wFMheeJ+YdGMReXvsjEVMBMGCSqGSIb3DQEJFTEGBAQBAAAA + """) + + pfx, tail = PFX().decode(self.pfx_raw) + self.assertSequenceEqual(tail, b"") + _, outer_safe_contents = pfx["authSafe"]["content"].defined + _, encrypted_data = outer_safe_contents[1]["bagValue"].defined + _, pbes2_params = encrypted_data["encryptedContentInfo"]["contentEncryptionAlgorithm"]["parameters"].defined + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + key = gost34112012_pbkdf2( + password=self.password.encode("utf-8"), + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("0e93d71339e7f53b79a0bc41f9109dd4fb60b30ae10736c1bb77b84c07681cfc") + self.assertSequenceEqual( + cfb_decrypt( + key, + bytes(encrypted_data["encryptedContentInfo"]["encryptedContent"]), + iv=bytes(enc_scheme_params["iv"]), + sbox="id-tc26-gost-28147-param-Z", + ), + cert_bag_expected, + ) + + def test_mac(self): + pfx, tail = PFX().decode(self.pfx_raw) + self.assertSequenceEqual(tail, b"") + _, outer_safe_contents = pfx["authSafe"]["content"].defined + mac_data = pfx["macData"] + mac_key = gost34112012_pbkdf2( + password=self.password.encode("utf-8"), + salt=bytes(mac_data["macSalt"]), + iterations=int(mac_data["iterations"]), + dklen=96, + )[-32:] + # mac_key = hexdec("cadbfbf3bceaa9b79f651508fac5abbeb4a13d0bd0e1876bd3c3efb2112128a5") + self.assertSequenceEqual( + hmac_new( + key=mac_key, + msg=SafeContents(outer_safe_contents).encode(), + digestmod=GOST34112012512, + ).digest(), + bytes(mac_data["mac"]["digest"]), + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestPFX2020(TestCase): + """PFX test vectors from newer PKCS#12 update + """ + ca_prv_raw = hexdec("092F8D059E97E22B90B1AE99F0087FC4D26620B91550CBB437C191005A290810") + ca_curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"] + ca_cert = Certificate().decod(b64decode(b""" + MIIB+TCCAaagAwIBAgIEAYy6gTAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS + cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx + MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA4MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx + 5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMHAQEBATALBgkq + hQMHAQIBAQEDQwAEQBpKgpyPDnhQAJyLqy8Qs0XQhgxEhby6tSypqYimgbjpcKqtU6 + 4jpDXc3h3BxGxtl2oHJ/4YLZ/ll87dto3ltMqjgZgwgZUwYwYDVR0jBFwwWoAUrGwO + TERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHk + NBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUrGwO + TERmokKW4p8JOyVm88ukUyowDwYDVR0TAQH/BAUwAwEB/zAKBggqhQMHAQEDAgNBAB + Gg3nhgQ5oCKbqlEdVaRxH+1WX4wVkawGXuTYkr1AC2OWw3ZC14Vvg3nazm8UMWUZtk + vu1kJcHQ4jFKkjUeg2E= + """)) + ca_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes( + ca_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"] + )))) + password = u"Пароль для PFX".encode("utf-8") + cert_test = Certificate().decod(b64decode(b""" + MIICLjCCAdugAwIBAgIEAYy6hDAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2MS + cwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEwMTAx + MDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYDVQQDEy + FPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDUxMi1iaXQwgaAwFwYIKoUDBwEBAQIw + CwYJKoUDBwECAQIBA4GEAASBgLSLt1q8KQ4YZVxioU+1LV9QhE7MHR9gBEh7S1yVNG + lqt7+rNG5VFqmrPM74rbUsOlhV8M+zZKprXdk35Oz8lSW/n2oIUHZxikXIH/SSHj4r + v3K/Puvz7hYTQSZl/xPdp78nUmjrEa6d5wfX8biEy2z0dgufFvAkMw1Ua4gdXqDOo4 + GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4MQ0wCwYD + VQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaX + SCBAGMuoEwHQYDVR0OBBYEFH4GVwmYDK1rCKhX7nkAWDrJ16CkMAoGCCqFAwcBAQMC + A0EACl6p8dAbpi9Hk+3mgMyI0WIh17IrlrSp/mB0F7ZzMt8XUD1Dwz3JrrnxeXnfMv + OA5BdUJ9hCyDgMVAGs/IcEEA== + """)) + prv_test_raw = b64decode(""" + MIHiAgEBMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQRAEWkl+eblsHWs86SNgRKq + SxMOgGhbvR/uZ5/WWfdNG1axvUwVhpcXIxDZUmzQuNzqJBkseI7f5/JjXyTFRF1a + +YGBgQG0i7davCkOGGVcYqFPtS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO + +K21LDpYVfDPs2Sqa13ZN+Ts/JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0Em + Zf8T3ae/J1Jo6xGunecH1/G4hMts9HYLnxbwJDMNVGuIHV6gzg== + """) + + def test_cert_and_encrypted_key(self): + pfx_raw = b64decode(b""" + MIIFKwIBAzCCBMQGCSqGSIb3DQEHAaCCBLUEggSxMIIErTCCAswGCSqGSIb3DQEH + AaCCAr0EggK5MIICtTCCArEGCyqGSIb3DQEMCgEDoIICSjCCAkYGCiqGSIb3DQEJ + FgGgggI2BIICMjCCAi4wggHboAMCAQICBAGMuoQwCgYIKoUDBwEBAwIwODENMAsG + A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt + Yml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UEChME + VEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiA1MTItYml0 + MIGgMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAQOBhAAEgYC0i7davCkOGGVcYqFP + tS1fUIROzB0fYARIe0tclTRpare/qzRuVRapqzzO+K21LDpYVfDPs2Sqa13ZN+Ts + /JUlv59qCFB2cYpFyB/0kh4+K79yvz7r8+4WE0EmZf8T3ae/J1Jo6xGunecH1/G4 + hMts9HYLnxbwJDMNVGuIHV6gzqOBhzCBhDBjBgNVHSMEXDBagBSsbA5MRGaiQpbi + nwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsy + NjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqBMB0GA1UdDgQWBBR+BlcJmAyt + awioV+55AFg6ydegpDAKBggqhQMHAQEDAgNBAApeqfHQG6YvR5Pt5oDMiNFiIdey + K5a0qf5gdBe2czLfF1A9Q8M9ya658Xl53zLzgOQXVCfYQsg4DFQBrPyHBBAxVDAj + BgkqhkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkU + MSAeHgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTCCAdkGCSqGSIb3DQEH + AaCCAcoEggHGMIIBwjCCAb4GCyqGSIb3DQEMCgECoIIBVzCCAVMwWQYJKoZIhvcN + AQUNMEwwKQYJKoZIhvcNAQUMMBwECKf4N7NMwugqAgIIADAMBggqhQMHAQEEAgUA + MB8GCSqFAwcBAQUCAjASBBAlmt2WDfaPJlsAs0mLKglzBIH1DMvEacbbWRNDVSnX + JLWygYrKoipdOjDA/2HEnBZ34uFOLNheUqiKpCPoFpbR2GBiVYVTVK9ibiczgaca + EQYzDXtcS0QCZOxpKWfteAlbdJLC/SqPurPYyKi0MVRUPROhbisFASDT38HDH1Dh + 0dL5f6ga4aPWLrWbbgWERFOoOPyh4DotlPF37AQOwiEjsbyyRHq3HgbWiaxQRuAh + eqHOn4QVGY92/HFvJ7u3TcnQdLWhTe/lh1RHLNF3RnXtN9if9zC23laDZOiWZplU + yLrUiTCbHrtn1RppPDmLFNMt9dJ7KKgCkOi7Zm5nhqPChbywX13wcfYxVDAjBgkq + hkiG9w0BCRUxFgQUeVV0+dS25MICJChpmGc/8AoUwE0wLQYJKoZIhvcNAQkUMSAe + HgBwADEAMgBGAHIAaQBlAG4AZABsAHkATgBhAG0AZTBeME4wCgYIKoUDBwEBAgME + QAkBKw4ihn7pSIYTEhu0bcvTPZjI3WgVxCkUVlOsc80G69EKFEOTnObGJGSKJ51U + KkOsXF0a7+VBZf3BcVVQh9UECIVEtO+VpuskAgIIAA== + """) + pfx = PFX().decod(pfx_raw) + _, outer_safe_contents = pfx["authSafe"]["content"].defined + + safe_contents = OctetStringSafeContents().decod(bytes( + outer_safe_contents[0]["bagValue"] + )) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag) + cert_bag = CertBag().decod(bytes(safe_bag["bagValue"])) + self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate) + _, cert = cert_bag["certValue"].defined + self.assertEqual(Certificate(cert), self.cert_test) + + safe_contents = OctetStringSafeContents().decod(bytes( + outer_safe_contents[1]["bagValue"] + )) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag) + shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"])) + _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + ukm = bytes(enc_scheme_params["ukm"]) + key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("4b7ae649ca31dd5fe3243a91a5188c03f1d7049bec8e0d241c0e1e8c39ea4c1f") + key_enc, key_mac = kdf_tree_gostr3411_2012_256( + key, b"kdf tree", ukm[GOST3412Kuznechik.blocksize // 2:], 2, + ) + ciphertext = bytes(shrouded_key_bag["encryptedData"]) + plaintext = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(key_enc).encrypt, + section_size=256 * 1024, + bs=GOST3412Kuznechik.blocksize, + data=ciphertext, + iv=ukm[:GOST3412Kuznechik.blocksize // 2], + ) + mac_expected = plaintext[-GOST3412Kuznechik.blocksize:] + plaintext = plaintext[:-GOST3412Kuznechik.blocksize] + mac = omac( + GOST3412Kuznechik(key_mac).encrypt, + GOST3412Kuznechik.blocksize, + plaintext, + ) + self.assertSequenceEqual(mac, mac_expected) + self.assertSequenceEqual(plaintext, self.prv_test_raw) + + mac_data = pfx["macData"] + mac_key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(mac_data["macSalt"]), + iterations=int(mac_data["iterations"]), + dklen=96, + )[-32:] + # mac_key = hexdec("a81d1bc91a4a5cf1fd7320f92dda7e5b285816c3b20826a382d7ed0cbf3a9bf4") + self.assertSequenceEqual( + hmac_new( + key=mac_key, + msg=SafeContents(outer_safe_contents).encode(), + digestmod=GOST34112012512, + ).digest(), + bytes(mac_data["mac"]["digest"]), + ) + self.assertTrue(gost3410.verify( + self.ca_curve, + self.ca_pub, + GOST34112012256(cert["tbsCertificate"].encode()).digest()[::-1], + bytes(cert["signatureValue"]), + )) + + def test_encrypted_cert_and_key(self): + pfx_raw = b64decode(b""" + MIIFjAIBAzCCBSUGCSqGSIb3DQEHAaCCBRYEggUSMIIFDjCCA0EGCSqGSIb3DQEH + BqCCAzIwggMuAgEAMIIDJwYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBIMCkGCSqG + SIb3DQEFDDAcBAgUuSVGsSwGjQICCAAwDAYIKoUDBwEBBAIFADAbBgkqhQMHAQEF + AQIwDgQM9Hk3dagtS48+G/x+gIICwWGPqxxN+sTrKbruRf9R5Ya9cf5AtO1frqMn + f1eULfmZmTg/BdE51QQ+Vbnh3v1kmspr6h2+e4Wli+ndEeCWG6A6X/G22h/RAHW2 + YrVmf6cCWxW+YrqzT4h/8RQL/9haunD5LmHPLVsYrEai0OwbgXayDSwARVJQLQYq + sLNmZK5ViN+fRiS5wszVJ3AtVq8EuPt41aQEKwPy2gmH4S6WmnQRC6W7aoqmIifF + PJENJNn5K2M1J6zNESs6bFtYNKMArNqtvv3rioY6eAaaLy6AV6ljsekmqodHmQjv + Y4eEioJs0xhpXhZY69PXT+ZBeHv6MSheBhwXqxAd1DqtPTafMjNK8rqKCap9TtPG + vONvo5W9dgwegxRRQzlum8dzV4m1W9Aq4W7t8/UcxDWRz3k6ijFPlGaA9+8ZMTEO + RHhBRvM6OY2/VNNxbgxWfGYuPxpSi3YnCZIPmBEe5lU/Xv7KjzFusGM38F8YR61k + 4/QNpKI1QUv714YKfaUQznshGGzILv1NGID62pl1+JI3vuawi2mDMrmkuM9QFU9v + /kRP+c2uBHDuOGEUUSNhF08p7+w3vxplatGWXH9fmIsPBdk2f3wkn+rwoqrEuijM + I/bCAylU/M0DMKhAo9j31UYSZdi4fsfRWYDJMq/8FPn96tuo+oCpbqv3NUwpZM/8 + Li4xqgTHtYw/+fRG0/P6XadNEiII/TYjenLfVHXjAHOVJsVeCu/t3EsMYHQddNCh + rFk/Ic2PdIQOyB4/enpW0qrKegSbyZNuF1WI4zl4mI89L8dTQBUkhy45yQXZlDD8 + k1ErYdtdEsPtz/4zuSpbnmwCEIRoOuSXtGuJP+tbcWEXRKM2UBgi3qBjpn7DU18M + tsrRM9pDdadl8mT/Vfh9+B8dZBZVxgQu70lMPEGexbUkYHuFCCnyi9J0V92StbIz + Elxla1VebjCCAcUGCSqGSIb3DQEHAaCCAbYEggGyMIIBrjCCAaoGCyqGSIb3DQEM + CgECoIIBQzCCAT8wVQYJKoZIhvcNAQUNMEgwKQYJKoZIhvcNAQUMMBwECP0EQk0O + 1twvAgIIADAMBggqhQMHAQEEAgUAMBsGCSqFAwcBAQUBATAOBAzwxSqgAAAAAAAA + AAAEgeUqj9mI3RDfK5hMd0EeYws7foZK/5ANr2wUhP5qnDjAZgn76lExJ+wuvlnS + 9PChfWVugvdl/9XJgQvvr9Cu4pOh4ICXplchcy0dGk/MzItHRVC5wK2nTxwQ4kKT + kG9xhLFzoD16dhtqX0+/dQg9G8pE5EzCBIYRXLm1Arcz9k7KVsTJuNMjFrr7EQuu + Tr80ATSQOtsq50zpFyrpznVPGCrOdIjpymZxNdvw48bZxqTtRVDxCYATOGqz0pwH + ClWULHD9LIajLMB2GhBKyQw6ujIlltJs0T+WNdX/AT2FLi1LFSS3+Cj9MVQwIwYJ + KoZIhvcNAQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMC0GCSqGSIb3DQEJFDEg + Hh4AcAAxADIARgByAGkAZQBuAGQAbAB5AE4AYQBtAGUwXjBOMAoGCCqFAwcBAQID + BEDp4e22JmXdnvR0xA99yQuzQuJ8pxBeOpsLm2dZQqt3Fje5zqW1uk/7VOcfV5r2 + bKm8nsLOs2rPT8hBOoeAZvOIBAjGIUHw6IjG2QICCAA= + """) + pfx = PFX().decod(pfx_raw) + _, outer_safe_contents = pfx["authSafe"]["content"].defined + + encrypted_data = EncryptedData().decod(bytes( + outer_safe_contents[0]["bagValue"] + )) + eci = encrypted_data["encryptedContentInfo"] + self.assertEqual(eci["contentEncryptionAlgorithm"]["algorithm"], id_pbes2) + pbes2_params = PBES2Params().decod(bytes( + eci["contentEncryptionAlgorithm"]["parameters"] + )) + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + ukm = bytes(enc_scheme_params["ukm"]) + key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("d066a96fb326ba896a2352d3f40240a4ded6e7e7bd5b4db6b5241d631c8c381c") + key_enc, key_mac = kdf_tree_gostr3411_2012_256( + key, b"kdf tree", ukm[GOST3412Magma.blocksize // 2:], 2, + ) + ciphertext = bytes(eci["encryptedContent"]) + plaintext = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(key_enc).encrypt, + section_size=8 * 1024, + bs=GOST3412Magma.blocksize, + data=ciphertext, + iv=ukm[:GOST3412Magma.blocksize // 2], + ) + mac_expected = plaintext[-GOST3412Magma.blocksize:] + plaintext = plaintext[:-GOST3412Magma.blocksize] + mac = omac( + GOST3412Magma(key_mac).encrypt, + GOST3412Magma.blocksize, + plaintext, + ) + self.assertSequenceEqual(mac, mac_expected) + + safe_contents = SafeContents().decod(plaintext) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag) + cert_bag = CertBag().decod(bytes(safe_bag["bagValue"])) + self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate) + _, cert = cert_bag["certValue"].defined + self.assertEqual(Certificate(cert), self.cert_test) + + safe_contents = OctetStringSafeContents().decod(bytes( + outer_safe_contents[1]["bagValue"] + )) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_pkcs8ShroudedKeyBag) + shrouded_key_bag = PKCS8ShroudedKeyBag().decod(bytes(safe_bag["bagValue"])) + _, pbes2_params = shrouded_key_bag["encryptionAlgorithm"]["parameters"].defined + _, pbkdf2_params = pbes2_params["keyDerivationFunc"]["parameters"].defined + _, enc_scheme_params = pbes2_params["encryptionScheme"]["parameters"].defined + ukm = bytes(enc_scheme_params["ukm"]) + key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(pbkdf2_params["salt"]["specified"]), + iterations=int(pbkdf2_params["iterationCount"]), + dklen=32, + ) + # key = hexdec("f840d001fd11441e0fb7ccf48f471915e5bf35275309dbe7ade9da4fe460ba7e") + ciphertext = bytes(shrouded_key_bag["encryptedData"]) + plaintext = ctr_acpkm( + GOST3412Magma, + GOST3412Magma(key).encrypt, + section_size=8 * 1024, + bs=GOST3412Magma.blocksize, + data=ciphertext, + iv=ukm[:GOST3412Magma.blocksize // 2], + ) + self.assertSequenceEqual(plaintext, self.prv_test_raw) + + mac_data = pfx["macData"] + mac_key = gost34112012_pbkdf2( + password=self.password, + salt=bytes(mac_data["macSalt"]), + iterations=int(mac_data["iterations"]), + dklen=96, + )[-32:] + # mac_key = hexdec("084f81782af1534ffd67e3c579c14cb45d7a6f659f46fdbb51a552e874e66fb2") + self.assertSequenceEqual( + hmac_new( + key=mac_key, + msg=SafeContents(outer_safe_contents).encode(), + digestmod=GOST34112012512, + ).digest(), + bytes(mac_data["mac"]["digest"]), + ) + + def test_dh(self): + curve = gost3410.CURVES["id-tc26-gost-3410-12-256-paramSetA"] + # sender_prv_raw = hexdec("0B20810E449978C7C3B76C6FF77A16C532421139344A058EF56310B6B6F377E8") + sender_cert = Certificate().decod(b64decode(""" + MIIB6zCCAZigAwIBAgIEAYy6gjAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 + MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw + MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA7MQ0wCwYDVQQKEwRUSzI2MSowKAYD + VQQDEyFPUklHSU5BVE9SOiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwXjAXBggqhQMH + AQEBATALBgkqhQMHAQIBAQEDQwAEQJYpDRNiWWqDgaZje0EmLLOldQ35o5X1ZuZN + SKequYQc/soI3OgDMWD7ThJJCk01IelCeb6MsBmG4lol+pnpVtOjgYcwgYQwYwYD + VR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88ukUyqhPKQ6MDgxDTALBgNVBAoTBFRL + MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdIIEAYy6 + gTAdBgNVHQ4EFgQUPx5RgcjkifhlJm4/jQdkbm30rVQwCgYIKoUDBwEBAwIDQQA6 + 8x7Vk6PvP/8xOGHhf8PuqaXAYskSyJPuBu+3Bo/PEj10devwc1J9uYWIDCGdKKPy + bSlnQHqUPBBPM30YX1YN + """)) + recipient_prv_raw = hexdec("0DC8DC1FF2BC114BABC3F1CA8C51E4F58610427E197B1C2FBDBA4AE58CBFB7CE")[::-1] + recipient_prv = gost3410.prv_unmarshal(recipient_prv_raw) + recipient_cert = Certificate().decod(b64decode(""" + MIIB6jCCAZegAwIBAgIEAYy6gzAKBggqhQMHAQEDAjA4MQ0wCwYDVQQKEwRUSzI2 + MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQwHhcNMDEw + MTAxMDAwMDAwWhcNNDkxMjMxMDAwMDAwWjA6MQ0wCwYDVQQKEwRUSzI2MSkwJwYD + VQQDEyBSRUNJUElFTlQ6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDBeMBcGCCqFAwcB + AQEBMAsGCSqFAwcBAgEBAQNDAARAvyeCGXMsYwpYe5aE0w8w3m4vpKQapGInqpnF + lv7h08psFP0s1W80q3BR534F4TmR+o5+iU+AW6ycvWuc73JEQ6OBhzCBhDBjBgNV + HSMEXDBagBSsbA5MRGaiQpbinwk7JWbzy6RTKqE8pDowODENMAsGA1UEChMEVEsy + NjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0ggQBjLqB + MB0GA1UdDgQWBBQ35gHPN1bx8l2eEMTbrtIg+5MU0TAKBggqhQMHAQEDAgNBABF2 + RHDaRqQuBS2yu7yGIGFgA6c/LG4GKjSOwYsRVmXJNNkQ4TB7PB8j3q7gx2koPsVB + m90WfMWSL6SNSh3muuM= + """)) + self.assertTrue(gost3410.verify( + self.ca_curve, + self.ca_pub, + GOST34112012256(sender_cert["tbsCertificate"].encode()).digest()[::-1], + bytes(sender_cert["signatureValue"]), + )) + self.assertTrue(gost3410.verify( + self.ca_curve, + self.ca_pub, + GOST34112012256(recipient_cert["tbsCertificate"].encode()).digest()[::-1], + bytes(recipient_cert["signatureValue"]), + )) + + pfx_raw = b64decode(""" + MIIKVwIBAzCCClAGCSqGSIb3DQEHAqCCCkEwggo9AgEBMQwwCgYIKoUDBwEBAgIw + ggcjBgkqhkiG9w0BBwGgggcUBIIHEDCCBwwwggKdBgkqhkiG9w0BBwGgggKOBIIC + ijCCAoYwggKCBgsqhkiG9w0BDAoBA6CCAkowggJGBgoqhkiG9w0BCRYBoIICNgSC + AjIwggIuMIIB26ADAgECAgQBjLqEMAoGCCqFAwcBAQMCMDgxDTALBgNVBAoTBFRL + MjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1QgMzQuMTAtMTIgMjU2LWJpdDAeFw0w + MTAxMDEwMDAwMDBaFw00OTEyMzEwMDAwMDBaMDsxDTALBgNVBAoTBFRLMjYxKjAo + BgNVBAMTIU9SSUdJTkFUT1I6IEdPU1QgMzQuMTAtMTIgNTEyLWJpdDCBoDAXBggq + hQMHAQEBAjALBgkqhQMHAQIBAgEDgYQABIGAtIu3WrwpDhhlXGKhT7UtX1CETswd + H2AESHtLXJU0aWq3v6s0blUWqas8zvittSw6WFXwz7Nkqmtd2Tfk7PyVJb+faghQ + dnGKRcgf9JIePiu/cr8+6/PuFhNBJmX/E92nvydSaOsRrp3nB9fxuITLbPR2C58W + 8CQzDVRriB1eoM6jgYcwgYQwYwYDVR0jBFwwWoAUrGwOTERmokKW4p8JOyVm88uk + UyqhPKQ6MDgxDTALBgNVBAoTBFRLMjYxJzAlBgNVBAMTHkNBIFRLMjY6IEdPU1Qg + MzQuMTAtMTIgMjU2LWJpdIIEAYy6gTAdBgNVHQ4EFgQUfgZXCZgMrWsIqFfueQBY + OsnXoKQwCgYIKoUDBwEBAwIDQQAKXqnx0BumL0eT7eaAzIjRYiHXsiuWtKn+YHQX + tnMy3xdQPUPDPcmuufF5ed8y84DkF1Qn2ELIOAxUAaz8hwQQMSUwIwYJKoZIhvcN + AQkVMRYEFHlVdPnUtuTCAiQoaZhnP/AKFMBNMIIEZwYJKoZIhvcNAQcDoIIEWDCC + BFQCAQKgggHzoIIB7zCCAeswggGYoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODEN + MAsGA1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAy + NTYtYml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UE + ChMEVEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYt + Yml0MF4wFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBA0MABECWKQ0TYllqg4GmY3tB + JiyzpXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ + 6VbTo4GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4 + MQ0wCwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEy + IDI1Ni1iaXSCBAGMuoEwHQYDVR0OBBYEFD8eUYHI5In4ZSZuP40HZG5t9K1UMAoG + CCqFAwcBAQMCA0EAOvMe1ZOj7z//MThh4X/D7qmlwGLJEsiT7gbvtwaPzxI9dHXr + 8HNSfbmFiAwhnSij8m0pZ0B6lDwQTzN9GF9WDTGB/6GB/AIBA6BCMEAwODENMAsG + A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt + Yml0AgQBjLqCoSIEIBt4fjey+k8C1D3OaMca8wl6h3j3C6OAbrx8rmxXktsQMBcG + CSqFAwcBAQcCATAKBggqhQMHAQEGATB2MHQwQDA4MQ0wCwYDVQQKEwRUSzI2MScw + JQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1Ni1iaXQCBAGMuoMEMJkp + Wae6IVfaY3mP0izRY7ifc41fATXdJ2tmTl+1vitkSE2vLCKXDLl90KfHA6gNmDCC + AVQGCSqGSIb3DQEHATAfBgkqhQMHAQEFAgEwEgQQFhEshEBO2LkAAAAAAAAAAICC + ASQYvLpT/8azEXJfekyGuyvE9UkVX+Ao8sfu9My/c4WAVRNMhZkCqD+BbPwBsIzN + sXZIi9rXGAfsPz7xaO9EUFZPjNOWtF/E01oJgG+gYLFn7qAiEFcmRLptSHuanNHn + 7Yol6IHushX4UaW9hEa/L6eFQx/hoDhrNZnWTXNZtNuHuMGC9dzhHhTxfkdjZYXD + v+M7psVj58JutE3U2d4pgxKcBPdMO4vl4+27cIKxQZFZU2zuCVJLYLqmPT5pCBkM + mJqy7bZwHOJ9kBq/TGUf8iJGYSCNre3RTNLbcTTk7rZrbiMkFsG3borzenpouS5E + BcCkBt8Mj0nvsMCu9ipHTuWww7LltlkXCjlNXFUi6ZI3VyHW5CDpghujQWiZxiAc + JuGl6GwZoIIB7zCCAeswggGYoAMCAQICBAGMuoIwCgYIKoUDBwEBAwIwODENMAsG + A1UEChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYt + Yml0MB4XDTAxMDEwMTAwMDAwMFoXDTQ5MTIzMTAwMDAwMFowOzENMAsGA1UEChME + VEsyNjEqMCgGA1UEAxMhT1JJR0lOQVRPUjogR09TVCAzNC4xMC0xMiAyNTYtYml0 + MF4wFwYIKoUDBwEBAQEwCwYJKoUDBwECAQEBA0MABECWKQ0TYllqg4GmY3tBJiyz + pXUN+aOV9WbmTUinqrmEHP7KCNzoAzFg+04SSQpNNSHpQnm+jLAZhuJaJfqZ6VbT + o4GHMIGEMGMGA1UdIwRcMFqAFKxsDkxEZqJCluKfCTslZvPLpFMqoTykOjA4MQ0w + CwYDVQQKEwRUSzI2MScwJQYDVQQDEx5DQSBUSzI2OiBHT1NUIDM0LjEwLTEyIDI1 + Ni1iaXSCBAGMuoEwHQYDVR0OBBYEFD8eUYHI5In4ZSZuP40HZG5t9K1UMAoGCCqF + AwcBAQMCA0EAOvMe1ZOj7z//MThh4X/D7qmlwGLJEsiT7gbvtwaPzxI9dHXr8HNS + fbmFiAwhnSij8m0pZ0B6lDwQTzN9GF9WDTGCAQ4wggEKAgEBMEAwODENMAsGA1UE + ChMEVEsyNjEnMCUGA1UEAxMeQ0EgVEsyNjogR09TVCAzNC4xMC0xMiAyNTYtYml0 + AgQBjLqCMAoGCCqFAwcBAQICoGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAc + BgkqhkiG9w0BCQUxDxcNMjEwNDE0MTkyMTEyWjAvBgkqhkiG9w0BCQQxIgQg1XOA + zNa710QuXsn5+yIf3cNTiFOQMgTiBRJBz8Tr4I0wCgYIKoUDBwEBAQEEQALINal9 + 7wHXYiG+w0yzSkKOs0jRZew0S73r/cfk/sUoM3HKKIEbKruvlAdiOqX/HLFSEx/s + kxFG6QUFH8uuoX8= + """) + pfx = PFX().decod(pfx_raw) + self.assertEqual(pfx["authSafe"]["contentType"], id_signedData) + + sd = SignedData().decod(bytes(pfx["authSafe"]["content"])) + self.assertEqual(sd["certificates"][0]["certificate"], sender_cert) + si = sd["signerInfos"][0] + self.assertEqual( + si["digestAlgorithm"]["algorithm"], + id_tc26_gost3411_2012_256, + ) + digest = [ + bytes(attr["attrValues"][0].defined[1]) for attr in si["signedAttrs"] + if attr["attrType"] == id_messageDigest + ][0] + sender_pub = gost3410.pub_unmarshal(bytes(OctetString().decod(bytes( + sender_cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"] + )))) + content = bytes(sd["encapContentInfo"]["eContent"]) + self.assertSequenceEqual(digest, GOST34112012256(content).digest()) + self.assertTrue(gost3410.verify( + curve, + sender_pub, + GOST34112012256( + SignedAttributes(si["signedAttrs"]).encode() + ).digest()[::-1], + bytes(si["signature"]), + )) + + outer_safe_contents = SafeContents().decod(content) + + safe_bag = outer_safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_data) + safe_contents = OctetStringSafeContents().decod(bytes(safe_bag["bagValue"])) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_certBag) + cert_bag = CertBag().decod(bytes(safe_bag["bagValue"])) + self.assertEqual(cert_bag["certId"], id_pkcs9_certTypes_x509Certificate) + _, cert = cert_bag["certValue"].defined + self.assertEqual(Certificate(cert), self.cert_test) + + safe_bag = outer_safe_contents[1] + self.assertEqual(safe_bag["bagId"], id_envelopedData) + ed = EnvelopedData().decod(bytes(safe_bag["bagValue"])) + kari = ed["recipientInfos"][0]["kari"] + ukm = bytes(kari["ukm"]) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_wrap_kexp15, + ) + self.assertEqual( + kari["keyEncryptionAlgorithm"]["parameters"].defined[1]["algorithm"], + id_tc26_agreement_gost3410_2012_256, + ) + kexp = bytes(kari["recipientEncryptedKeys"][0]["encryptedKey"]) + keymat = keg(curve, recipient_prv, sender_pub, ukm) + kim, kek = keymat[:KEYSIZE], keymat[KEYSIZE:] + cek = kimp15( + GOST3412Kuznechik(kek).encrypt, + GOST3412Kuznechik(kim).encrypt, + GOST3412Kuznechik.blocksize, + kexp, + ukm[24:24 + GOST3412Kuznechik.blocksize // 2], + ) + eci = ed["encryptedContentInfo"] + self.assertEqual( + eci["contentEncryptionAlgorithm"]["algorithm"], + id_gostr3412_2015_kuznyechik_ctracpkm, + ) + eci_ukm = bytes( + eci["contentEncryptionAlgorithm"]["parameters"].defined[1]["ukm"] + ) + content = ctr_acpkm( + GOST3412Kuznechik, + GOST3412Kuznechik(cek).encrypt, + 256 * 1024, + GOST3412Kuznechik.blocksize, + bytes(eci["encryptedContent"]), + eci_ukm[:GOST3412Kuznechik.blocksize // 2], + ) + + safe_contents = SafeContents().decod(content) + safe_bag = safe_contents[0] + self.assertEqual(safe_bag["bagId"], id_pkcs12_bagtypes_keyBag) + KeyBag().decod(bytes(safe_bag["bagValue"])) + self.assertSequenceEqual(bytes(safe_bag["bagValue"]), self.prv_test_raw) diff --git a/pygost-5.13/pygost/test_wrap.py b/pygost-5.13/pygost/test_wrap.py new file mode 100644 index 0000000..7ecf8ee --- /dev/null +++ b/pygost-5.13/pygost/test_wrap.py @@ -0,0 +1,111 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from os import urandom +from unittest import TestCase + +from pygost.gost28147 import DEFAULT_SBOX +from pygost.gost3412 import GOST3412Kuznechik +from pygost.gost3412 import GOST3412Magma +from pygost.utils import hexdec +from pygost.wrap import kexp15 +from pygost.wrap import kimp15 +from pygost.wrap import unwrap_cryptopro +from pygost.wrap import unwrap_gost +from pygost.wrap import wrap_cryptopro +from pygost.wrap import wrap_gost + + +class WrapGostTest(TestCase): + def test_symmetric(self): + for sbox in (DEFAULT_SBOX, "id-tc26-gost-28147-param-Z"): + for _ in range(1 << 8): + kek = urandom(32) + cek = urandom(32) + ukm = urandom(8) + wrapped = wrap_gost(ukm, kek, cek, sbox=sbox) + unwrapped = unwrap_gost(kek, wrapped, sbox=sbox) + self.assertSequenceEqual(unwrapped, cek) + + def test_invalid_length(self): + with self.assertRaises(ValueError): + unwrap_gost(urandom(32), urandom(41)) + with self.assertRaises(ValueError): + unwrap_gost(urandom(32), urandom(45)) + + +class WrapCryptoproTest(TestCase): + def test_symmetric(self): + for sbox in (DEFAULT_SBOX, "id-tc26-gost-28147-param-Z"): + for _ in range(1 << 8): + kek = urandom(32) + cek = urandom(32) + ukm = urandom(8) + wrapped = wrap_cryptopro(ukm, kek, cek, sbox=sbox) + unwrapped = unwrap_cryptopro(kek, wrapped, sbox=sbox) + self.assertSequenceEqual(unwrapped, cek) + + +class TestVectorKExp15(TestCase): + """Test vectors from Р 1323565.1.017-2018 + """ + key = hexdec("8899AABBCCDDEEFF0011223344556677FEDCBA98765432100123456789ABCDEF") + key_enc = hexdec("202122232425262728292A2B2C2D2E2F38393A3B3C3D3E3F3031323334353637") + key_mac = hexdec("08090A0B0C0D0E0F0001020304050607101112131415161718191A1B1C1D1E1F") + + def test_magma(self): + iv = hexdec("67BED654") + kexp = kexp15( + GOST3412Magma(self.key_enc).encrypt, + GOST3412Magma(self.key_mac).encrypt, + GOST3412Magma.blocksize, + self.key, + iv, + ) + self.assertSequenceEqual(kexp, hexdec(""" +CF D5 A1 2D 5B 81 B6 E1 E9 9C 91 6D 07 90 0C 6A +C1 27 03 FB 3A BD ED 55 56 7B F3 74 2C 89 9C 75 +5D AF E7 B4 2E 3A 8B D9 + """.replace("\n", "").replace(" ", ""))) + self.assertSequenceEqual(kimp15( + GOST3412Magma(self.key_enc).encrypt, + GOST3412Magma(self.key_mac).encrypt, + GOST3412Magma.blocksize, + kexp, + iv, + ), self.key) + + def test_kuznechik(self): + iv = hexdec("0909472DD9F26BE8") + kexp = kexp15( + GOST3412Kuznechik(self.key_enc).encrypt, + GOST3412Kuznechik(self.key_mac).encrypt, + GOST3412Kuznechik.blocksize, + self.key, + iv, + ) + self.assertSequenceEqual(kexp, hexdec(""" +E3 61 84 E8 4E 8D 73 6F F3 6C C2 E5 AE 06 5D C6 +56 B2 3C 20 F5 49 B0 2F DF F8 8E 1F 3F 30 D8 C2 +9A 53 F3 CA 55 4D BA D8 0D E1 52 B9 A4 62 5B 32 + """.replace("\n", "").replace(" ", ""))) + self.assertSequenceEqual(kimp15( + GOST3412Kuznechik(self.key_enc).encrypt, + GOST3412Kuznechik(self.key_mac).encrypt, + GOST3412Kuznechik.blocksize, + kexp, + iv, + ), self.key) diff --git a/pygost-5.13/pygost/test_x509.py b/pygost-5.13/pygost/test_x509.py new file mode 100644 index 0000000..e9fccb7 --- /dev/null +++ b/pygost-5.13/pygost/test_x509.py @@ -0,0 +1,433 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from base64 import b64decode +from unittest import skipIf +from unittest import TestCase + +from pygost.gost3410 import CURVES +from pygost.gost3410 import prv_unmarshal +from pygost.gost3410 import pub_marshal +from pygost.gost3410 import pub_unmarshal +from pygost.gost3410 import public_key +from pygost.gost3410 import verify +from pygost.gost34112012256 import GOST34112012256 +from pygost.gost34112012512 import GOST34112012512 +from pygost.utils import hexdec + +try: + + from pyderasn import Any + from pyderasn import BitString + from pyderasn import Boolean + from pyderasn import GeneralizedTime + from pyderasn import Integer + from pyderasn import OctetString + from pyderasn import PrintableString + from pyderasn import UTCTime + + from pygost.asn1schemas.oids import id_at_commonName + from pygost.asn1schemas.oids import id_ce_basicConstraints + from pygost.asn1schemas.oids import id_GostR3410_2001_TestParamSet + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_256_paramSetA + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512 + from pygost.asn1schemas.oids import id_tc26_gost3410_2012_512_paramSetTest + from pygost.asn1schemas.oids import id_tc26_gost3411_2012_256 + from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_256 + from pygost.asn1schemas.oids import id_tc26_signwithdigest_gost3410_2012_512 + from pygost.asn1schemas.pkcs10 import Attributes + from pygost.asn1schemas.pkcs10 import CertificationRequest + from pygost.asn1schemas.pkcs10 import CertificationRequestInfo + from pygost.asn1schemas.x509 import AlgorithmIdentifier + from pygost.asn1schemas.x509 import AttributeType + from pygost.asn1schemas.x509 import AttributeTypeAndValue + from pygost.asn1schemas.x509 import AttributeValue + from pygost.asn1schemas.x509 import BasicConstraints + from pygost.asn1schemas.x509 import Certificate + from pygost.asn1schemas.x509 import CertificateList + from pygost.asn1schemas.x509 import CertificateSerialNumber + from pygost.asn1schemas.x509 import Extension + from pygost.asn1schemas.x509 import Extensions + from pygost.asn1schemas.x509 import GostR34102012PublicKeyParameters + from pygost.asn1schemas.x509 import Name + from pygost.asn1schemas.x509 import RDNSequence + from pygost.asn1schemas.x509 import RelativeDistinguishedName + from pygost.asn1schemas.x509 import SubjectPublicKeyInfo + from pygost.asn1schemas.x509 import TBSCertificate + from pygost.asn1schemas.x509 import TBSCertList + from pygost.asn1schemas.x509 import Time + from pygost.asn1schemas.x509 import Validity + from pygost.asn1schemas.x509 import Version + +except ImportError: + pyderasn_exists = False +else: + pyderasn_exists = True + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestCertificate(TestCase): + """Certificate test vectors from "Использования алгоритмов ГОСТ Р + 34.10, ГОСТ Р 34.11 в профиле сертификата и списке отзыва + сертификатов (CRL) инфраструктуры открытых ключей X.509" + (TK26IOK.pdf) + """ + + def process_cert(self, curve_name, hasher, prv_key_raw, cert_raw): + cert, tail = Certificate().decode(cert_raw, ctx={ + "defines_by_path": ( + ( + ( + "tbsCertificate", + "subjectPublicKeyInfo", + "algorithm", + "algorithm", + ), + ( + ( + ("..", "subjectPublicKey"), + { + id_tc26_gost3410_2012_256: OctetString(), + id_tc26_gost3410_2012_512: OctetString(), + }, + ), + ), + ), + ), + }) + self.assertSequenceEqual(tail, b"") + curve = CURVES[curve_name] + prv_key = prv_unmarshal(prv_key_raw) + spk = cert["tbsCertificate"]["subjectPublicKeyInfo"]["subjectPublicKey"] + self.assertIsNotNone(spk.defined) + _, pub_key_raw = spk.defined + pub_key = pub_unmarshal(bytes(pub_key_raw)) + self.assertSequenceEqual(pub_key, public_key(curve, prv_key)) + self.assertTrue(verify( + curve, + pub_key, + hasher(cert["tbsCertificate"].encode()).digest()[::-1], + bytes(cert["signatureValue"]), + )) + + def test_256(self): + cert_raw = b64decode(""" +MIICYjCCAg+gAwIBAgIBATAKBggqhQMHAQEDAjBWMSkwJwYJKoZIhvcNAQkBFhpH +b3N0UjM0MTAtMjAxMkBleGFtcGxlLmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIw +MTIgKDI1NiBiaXQpIGV4YW1wbGUwHhcNMTMxMTA1MTQwMjM3WhcNMzAxMTAxMTQw +MjM3WjBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxlLmNv +bTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgKDI1NiBiaXQpIGV4YW1wbGUwZjAf +BggqhQMHAQEBATATBgcqhQMCAiQABggqhQMHAQECAgNDAARAut/Qw1MUq9KPqkdH +C2xAF3K7TugHfo9n525D2s5mFZdD5pwf90/i4vF0mFmr9nfRwMYP4o0Pg1mOn5Rl +aXNYraOBwDCBvTAdBgNVHQ4EFgQU1fIeN1HaPbw+XWUzbkJ+kHJUT0AwCwYDVR0P +BAQDAgHGMA8GA1UdEwQIMAYBAf8CAQEwfgYDVR0BBHcwdYAU1fIeN1HaPbw+XWUz +bkJ+kHJUT0ChWqRYMFYxKTAnBgkqhkiG9w0BCQEWGkdvc3RSMzQxMC0yMDEyQGV4 +YW1wbGUuY29tMSkwJwYDVQQDEyBHb3N0UjM0MTAtMjAxMiAoMjU2IGJpdCkgZXhh +bXBsZYIBATAKBggqhQMHAQEDAgNBAF5bm4BbARR6hJLEoWJkOsYV3Hd7kXQQjz3C +dqQfmHrz6TI6Xojdh/t8ckODv/587NS5/6KsM77vc6Wh90NAT2s= + """) + prv_key_raw = hexdec("BFCF1D623E5CDD3032A7C6EABB4A923C46E43D640FFEAAF2C3ED39A8FA399924")[::-1] + self.process_cert( + "id-GostR3410-2001-CryptoPro-XchA-ParamSet", + GOST34112012256, + prv_key_raw, + cert_raw, + ) + + def test_512(self): + cert_raw = b64decode(""" +MIIC6DCCAlSgAwIBAgIBATAKBggqhQMHAQEDAzBWMSkwJwYJKoZIhvcNAQkBFhpH +b3N0UjM0MTAtMjAxMkBleGFtcGxlLmNvbTEpMCcGA1UEAxMgR29zdFIzNDEwLTIw +MTIgKDUxMiBiaXQpIGV4YW1wbGUwHhcNMTMxMDA0MDczNjA0WhcNMzAxMDAxMDcz +NjA0WjBWMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAxMkBleGFtcGxlLmNv +bTEpMCcGA1UEAxMgR29zdFIzNDEwLTIwMTIgKDUxMiBiaXQpIGV4YW1wbGUwgaow +IQYIKoUDBwEBAQIwFQYJKoUDBwECAQICBggqhQMHAQECAwOBhAAEgYATGQ9VCiM5 +FRGCQ8MEz2F1dANqhaEuywa8CbxOnTvaGJpFQVXQwkwvLFAKh7hk542vOEtxpKtT +CXfGf84nRhMH/Q9bZeAc2eO/yhxrsQhTBufa1Fuou2oe/jUOaG6RAtUUvRzhNTpp +RGGl1+EIY2vzzUua9j9Ol/gAoy/LNKQIfqOBwDCBvTAdBgNVHQ4EFgQUPcbTRXJZ +nHtjj+eBP7b5lcTMekIwCwYDVR0PBAQDAgHGMA8GA1UdEwQIMAYBAf8CAQEwfgYD +VR0BBHcwdYAUPcbTRXJZnHtjj+eBP7b5lcTMekKhWqRYMFYxKTAnBgkqhkiG9w0B +CQEWGkdvc3RSMzQxMC0yMDEyQGV4YW1wbGUuY29tMSkwJwYDVQQDEyBHb3N0UjM0 +MTAtMjAxMiAoNTEyIGJpdCkgZXhhbXBsZYIBATAKBggqhQMHAQEDAwOBgQBObS7o +ppPTXzHyVR1DtPa8b57nudJzI4czhsfeX5HDntOq45t9B/qSs8dC6eGxbhHZ9zCO +SFtxWYdmg0au8XI9Xb8vTC1qdwWID7FFjMWDNQZb6lYh/J+8F2xKylvB5nIlRZqO +o3eUNFkNyHJwQCk2WoOlO16zwGk2tdKH4KmD5w== + """) + prv_key_raw = hexdec("3FC01CDCD4EC5F972EB482774C41E66DB7F380528DFE9E67992BA05AEE462435757530E641077CE587B976C8EEB48C48FD33FD175F0C7DE6A44E014E6BCB074B")[::-1] + self.process_cert( + "id-tc26-gost-3410-12-512-paramSetB", + GOST34112012512, + prv_key_raw, + cert_raw, + ) + + +@skipIf(not pyderasn_exists, "PyDERASN dependency is required") +class TestRFC4491bis(TestCase): + """Test vectors from https://tools.ietf.org/html/draft-deremin-rfc4491-bis-02 + """ + + def _test_vector( + self, + curve_name, + hsh, + ai_spki, + ai_sign, + cert_serial, + prv_hex, + cr_sign_hex, + cr_b64, + c_sign_hex, + c_b64, + crl_sign_hex, + crl_b64, + ): + prv_raw = hexdec(prv_hex)[::-1] + prv = prv_unmarshal(prv_raw) + curve = CURVES[curve_name] + pub = public_key(curve, prv) + pub_raw = pub_marshal(pub) + subj = Name(("rdnSequence", RDNSequence([ + RelativeDistinguishedName(( + AttributeTypeAndValue(( + ("type", AttributeType(id_at_commonName)), + ("value", AttributeValue(PrintableString("Example"))), + )), + )) + ]))) + spki = SubjectPublicKeyInfo(( + ("algorithm", ai_spki), + ("subjectPublicKey", BitString(OctetString(pub_raw).encode())), + )) + + # Certification request + cri = CertificationRequestInfo(( + ("version", Integer(0)), + ("subject", subj), + ("subjectPKInfo", spki), + ("attributes", Attributes()), + )) + sign = hexdec(cr_sign_hex) + self.assertTrue(verify( + curve, + pub, + hsh(cri.encode()).digest()[::-1], + sign, + )) + cr = CertificationRequest(( + ("certificationRequestInfo", cri), + ("signatureAlgorithm", ai_sign), + ("signature", BitString(sign)), + )) + self.assertSequenceEqual(cr.encode(), b64decode(cr_b64)) + + # Certificate + tbs = TBSCertificate(( + ("version", Version("v3")), + ("serialNumber", CertificateSerialNumber(cert_serial)), + ("signature", ai_sign), + ("issuer", subj), + ("validity", Validity(( + ("notBefore", Time(("utcTime", UTCTime(b"010101000000Z")))), + ("notAfter", Time(("generalTime", GeneralizedTime(b"20501231000000Z")))), + ))), + ("subject", subj), + ("subjectPublicKeyInfo", spki), + ("extensions", Extensions(( + Extension(( + ("extnID", id_ce_basicConstraints), + ("critical", Boolean(True)), + ("extnValue", OctetString( + BasicConstraints((("cA", Boolean(True)),)).encode() + )), + )), + ))), + )) + sign = hexdec(c_sign_hex) + self.assertTrue(verify( + curve, + pub, + hsh(tbs.encode()).digest()[::-1], + sign, + )) + cert = Certificate(( + ("tbsCertificate", tbs), + ("signatureAlgorithm", ai_sign), + ("signatureValue", BitString(sign)), + )) + self.assertSequenceEqual(cert.encode(), b64decode(c_b64)) + + # CRL + tbs = TBSCertList(( + ("version", Version("v2")), + ("signature", ai_sign), + ("issuer", subj), + ("thisUpdate", Time(("utcTime", UTCTime(b"140101000000Z")))), + ("nextUpdate", Time(("utcTime", UTCTime(b"140102000000Z")))), + )) + sign = hexdec(crl_sign_hex) + self.assertTrue(verify( + curve, + pub, + hsh(tbs.encode()).digest()[::-1], + sign, + )) + crl = CertificateList(( + ("tbsCertList", tbs), + ("signatureAlgorithm", ai_sign), + ("signatureValue", BitString(sign)), + )) + self.assertSequenceEqual(crl.encode(), b64decode(crl_b64)) + + def test_256_test_paramset(self): + self._test_vector( + "id-GostR3410-2001-TestParamSet", + GOST34112012256, + AlgorithmIdentifier(( + ("algorithm", id_tc26_gost3410_2012_256), + ("parameters", Any( + GostR34102012PublicKeyParameters(( + ("publicKeyParamSet", id_GostR3410_2001_TestParamSet), + ("digestParamSet", id_tc26_gost3411_2012_256), + )) + )), + )), + AlgorithmIdentifier(( + ("algorithm", id_tc26_signwithdigest_gost3410_2012_256), + )), + 10, + "7A929ADE789BB9BE10ED359DD39A72C11B60961F49397EEE1D19CE9891EC3B28", + "6AAAB38E35D4AAA517940301799122D855484F579F4CBB96D63CDFDF3ACC432A41AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493", + """ +MIHTMIGBAgEAMBIxEDAOBgNVBAMTB0V4YW1wbGUwZjAfBggqhQMHAQEBATATBgcq +hQMCAiMABggqhQMHAQECAgNDAARAC9hv5djbiWaPeJtOHbqFhcVQi0XsW1nYkG3b +cOJJK3/ad/+HGhD73ydm0pPF0WSvuzx7lzpByIXRHXDWibTxJqAAMAoGCCqFAwcB +AQMCA0EAaqqzjjXUqqUXlAMBeZEi2FVIT1efTLuW1jzf3zrMQypBqijS8asUgoDN +ntVv7aQZdAU1VKQnZ7g60EP9OdwEkw== + """, + "4D53F012FE081776507D4D9BB81F00EFDB4EEFD4AB83BAC4BACF735173CFA81C41AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493", + """ +MIIBLTCB26ADAgECAgEKMAoGCCqFAwcBAQMCMBIxEDAOBgNVBAMTB0V4YW1wbGUw +IBcNMDEwMTAxMDAwMDAwWhgPMjA1MDEyMzEwMDAwMDBaMBIxEDAOBgNVBAMTB0V4 +YW1wbGUwZjAfBggqhQMHAQEBATATBgcqhQMCAiMABggqhQMHAQECAgNDAARAC9hv +5djbiWaPeJtOHbqFhcVQi0XsW1nYkG3bcOJJK3/ad/+HGhD73ydm0pPF0WSvuzx7 +lzpByIXRHXDWibTxJqMTMBEwDwYDVR0TAQH/BAUwAwEB/zAKBggqhQMHAQEDAgNB +AE1T8BL+CBd2UH1Nm7gfAO/bTu/Uq4O6xLrPc1Fzz6gcQaoo0vGrFIKAzZ7Vb+2k +GXQFNVSkJ2e4OtBD/TncBJM= + """, + "42BF392A14D3EBE957AF3E46CB50BF5F4221A003AD3D172753C94A9C37A31D2041AA28D2F1AB148280CD9ED56FEDA41974053554A42767B83AD043FD39DC0493", + """ +MIGSMEECAQEwCgYIKoUDBwEBAwIwEjEQMA4GA1UEAxMHRXhhbXBsZRcNMTQwMTAx +MDAwMDAwWhcNMTQwMTAyMDAwMDAwWjAKBggqhQMHAQEDAgNBAEK/OSoU0+vpV68+ +RstQv19CIaADrT0XJ1PJSpw3ox0gQaoo0vGrFIKAzZ7Vb+2kGXQFNVSkJ2e4OtBD +/TncBJM= + """, + ) + + def test_256a_paramset(self): + self._test_vector( + "id-tc26-gost-3410-2012-256-paramSetA", + GOST34112012256, + AlgorithmIdentifier(( + ("algorithm", id_tc26_gost3410_2012_256), + ("parameters", Any( + GostR34102012PublicKeyParameters(( + ("publicKeyParamSet", id_tc26_gost3410_2012_256_paramSetA), + )) + )), + )), + AlgorithmIdentifier(( + ("algorithm", id_tc26_signwithdigest_gost3410_2012_256), + )), + 10, + "7A929ADE789BB9BE10ED359DD39A72C11B60961F49397EEE1D19CE9891EC3B28", + "1BDC2A1317679B66232F63EA16FF7C64CCAAB9AD855FC6E18091661DB79D48121D0E1DA5BE347C6F1B5256C7AEAC200AD64AC77A6F5B3A0E097318E7AE6EE769", + """ +MIHKMHkCAQAwEjEQMA4GA1UEAxMHRXhhbXBsZTBeMBcGCCqFAwcBAQEBMAsGCSqF +AwcBAgEBAQNDAARAdCeV1L7ohN3yhQ/sA+o/rxhE4B2dpgtkUJOlXibfw5l49ZbP +TU0MbPHRiUPZRJPRa57AoW1RLS4SfMRpGmMY4qAAMAoGCCqFAwcBAQMCA0EAG9wq +Exdnm2YjL2PqFv98ZMyqua2FX8bhgJFmHbedSBIdDh2lvjR8bxtSVseurCAK1krH +em9bOg4Jcxjnrm7naQ== + """, + "140B4DA9124B09CB0D5CE928EE874273A310129492EC0E29369E3B791248578C1D0E1DA5BE347C6F1B5256C7AEAC200AD64AC77A6F5B3A0E097318E7AE6EE769", + """ +MIIBJTCB06ADAgECAgEKMAoGCCqFAwcBAQMCMBIxEDAOBgNVBAMTB0V4YW1wbGUw +IBcNMDEwMTAxMDAwMDAwWhgPMjA1MDEyMzEwMDAwMDBaMBIxEDAOBgNVBAMTB0V4 +YW1wbGUwXjAXBggqhQMHAQEBATALBgkqhQMHAQIBAQEDQwAEQHQnldS+6ITd8oUP +7APqP68YROAdnaYLZFCTpV4m38OZePWWz01NDGzx0YlD2UST0WuewKFtUS0uEnzE +aRpjGOKjEzARMA8GA1UdEwEB/wQFMAMBAf8wCgYIKoUDBwEBAwIDQQAUC02pEksJ +yw1c6Sjuh0JzoxASlJLsDik2njt5EkhXjB0OHaW+NHxvG1JWx66sIArWSsd6b1s6 +DglzGOeubudp + """, + "14BD68087C3B903C7AA28B07FEB2E7BD6FE0963F563267359F5CD8EAB45059AD1D0E1DA5BE347C6F1B5256C7AEAC200AD64AC77A6F5B3A0E097318E7AE6EE769", + """ +MIGSMEECAQEwCgYIKoUDBwEBAwIwEjEQMA4GA1UEAxMHRXhhbXBsZRcNMTQwMTAx +MDAwMDAwWhcNMTQwMTAyMDAwMDAwWjAKBggqhQMHAQEDAgNBABS9aAh8O5A8eqKL +B/6y571v4JY/VjJnNZ9c2Oq0UFmtHQ4dpb40fG8bUlbHrqwgCtZKx3pvWzoOCXMY +565u52k= + """, + ) + + def test_512_test_paramset(self): + self._test_vector( + "id-tc26-gost-3410-2012-512-paramSetTest", + GOST34112012512, + AlgorithmIdentifier(( + ("algorithm", id_tc26_gost3410_2012_512), + ("parameters", Any( + GostR34102012PublicKeyParameters(( + ("publicKeyParamSet", id_tc26_gost3410_2012_512_paramSetTest), + )) + )), + )), + AlgorithmIdentifier(( + ("algorithm", id_tc26_signwithdigest_gost3410_2012_512), + )), + 11, + "0BA6048AADAE241BA40936D47756D7C93091A0E8514669700EE7508E508B102072E8123B2200A0563322DAD2827E2714A2636B7BFD18AADFC62967821FA18DD4", + "433B1D6CE40A51F1E5737EB16AA2C683829A405B9D9127E21260FC9D6AC05D87BF24E26C45278A5C2192A75BA94993ABD6074E7FF1BF03FD2F5397AFA1D945582F86FA60A081091A23DD795E1E3C689EE512A3C82EE0DCC2643C78EEA8FCACD35492558486B20F1C9EC197C90699850260C93BCBCD9C5C3317E19344E173AE36", + """ +MIIBTzCBvAIBADASMRAwDgYDVQQDEwdFeGFtcGxlMIGgMBcGCCqFAwcBAQECMAsG +CSqFAwcBAgECAAOBhAAEgYDh7zDVLGEz3dmdHVxBRVz3302LTJJbvGmvFDPRVlhR +Wt0hRoUMMlxbgcEzvmVaqMTUQOe5io1ZSHsMdpa8xV0R7L53NqnsNX/y/TmTH04R +TLjNo1knCsfw5/9D2UGUGeph/Sq3f12fY1I9O1CgT2PioM9Rt8E63CFWDwvUDMnH +N6AAMAoGCCqFAwcBAQMDA4GBAEM7HWzkClHx5XN+sWqixoOCmkBbnZEn4hJg/J1q +wF2HvyTibEUnilwhkqdbqUmTq9YHTn/xvwP9L1OXr6HZRVgvhvpgoIEJGiPdeV4e +PGie5RKjyC7g3MJkPHjuqPys01SSVYSGsg8cnsGXyQaZhQJgyTvLzZxcMxfhk0Th +c642 + """, + "415703D892F1A5F3F68C4353189A7EE207B80B5631EF9D49529A4D6B542C2CFA15AA2EACF11F470FDE7D954856903C35FD8F955EF300D95C77534A724A0EEE702F86FA60A081091A23DD795E1E3C689EE512A3C82EE0DCC2643C78EEA8FCACD35492558486B20F1C9EC197C90699850260C93BCBCD9C5C3317E19344E173AE36", + """ +MIIBqjCCARagAwIBAgIBCzAKBggqhQMHAQEDAzASMRAwDgYDVQQDEwdFeGFtcGxl +MCAXDTAxMDEwMTAwMDAwMFoYDzIwNTAxMjMxMDAwMDAwWjASMRAwDgYDVQQDEwdF +eGFtcGxlMIGgMBcGCCqFAwcBAQECMAsGCSqFAwcBAgECAAOBhAAEgYDh7zDVLGEz +3dmdHVxBRVz3302LTJJbvGmvFDPRVlhRWt0hRoUMMlxbgcEzvmVaqMTUQOe5io1Z +SHsMdpa8xV0R7L53NqnsNX/y/TmTH04RTLjNo1knCsfw5/9D2UGUGeph/Sq3f12f +Y1I9O1CgT2PioM9Rt8E63CFWDwvUDMnHN6MTMBEwDwYDVR0TAQH/BAUwAwEB/zAK +BggqhQMHAQEDAwOBgQBBVwPYkvGl8/aMQ1MYmn7iB7gLVjHvnUlSmk1rVCws+hWq +LqzxH0cP3n2VSFaQPDX9j5Ve8wDZXHdTSnJKDu5wL4b6YKCBCRoj3XleHjxonuUS +o8gu4NzCZDx47qj8rNNUklWEhrIPHJ7Bl8kGmYUCYMk7y82cXDMX4ZNE4XOuNg== + """, + "3A13FB7AECDB5560EEF6137CFC5DD64691732EBFB3690A1FC0C7E8A4EEEA08307D648D4DC0986C46A87B3FBE4C7AF42EA34359C795954CA39FF3ABBED9051F4D2F86FA60A081091A23DD795E1E3C689EE512A3C82EE0DCC2643C78EEA8FCACD35492558486B20F1C9EC197C90699850260C93BCBCD9C5C3317E19344E173AE36", + """ +MIHTMEECAQEwCgYIKoUDBwEBAwMwEjEQMA4GA1UEAxMHRXhhbXBsZRcNMTQwMTAx +MDAwMDAwWhcNMTQwMTAyMDAwMDAwWjAKBggqhQMHAQEDAwOBgQA6E/t67NtVYO72 +E3z8XdZGkXMuv7NpCh/Ax+ik7uoIMH1kjU3AmGxGqHs/vkx69C6jQ1nHlZVMo5/z +q77ZBR9NL4b6YKCBCRoj3XleHjxonuUSo8gu4NzCZDx47qj8rNNUklWEhrIPHJ7B +l8kGmYUCYMk7y82cXDMX4ZNE4XOuNg== + """, + ) diff --git a/pygost-5.13/pygost/utils.py b/pygost-5.13/pygost/utils.py new file mode 100644 index 0000000..21c31b0 --- /dev/null +++ b/pygost-5.13/pygost/utils.py @@ -0,0 +1,101 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from codecs import getdecoder +from codecs import getencoder +from sys import version_info + + +xrange = range if version_info[0] == 3 else xrange + + +def strxor(a, b): + """XOR of two strings + + This function will process only shortest length of both strings, + ignoring remaining one. + """ + mlen = min(len(a), len(b)) + a, b, xor = bytearray(a), bytearray(b), bytearray(mlen) + for i in xrange(mlen): + xor[i] = a[i] ^ b[i] + return bytes(xor) + + +_hexdecoder = getdecoder("hex") +_hexencoder = getencoder("hex") + + +def hexdec(data): + """Decode hexadecimal + """ + return _hexdecoder(data)[0] + + +def hexenc(data): + """Encode hexadecimal + """ + return _hexencoder(data)[0].decode("ascii") + + +def bytes2long(raw): + """Deserialize big-endian bytes into long number + + :param bytes raw: binary string + :returns: deserialized long number + :rtype: int + """ + return int(hexenc(raw), 16) + + +def long2bytes(n, size=32): + """Serialize long number into big-endian bytestring + + :param long n: long number + :returns: serialized bytestring + :rtype: bytes + """ + res = hex(int(n))[2:].rstrip("L") + if len(res) % 2 != 0: + res = "0" + res + s = hexdec(res) + if len(s) != size: + s = (size - len(s)) * b"\x00" + s + return s + + +def modinvert(a, n): + """Modular multiplicative inverse + + :returns: inverse number. -1 if it does not exist + + Realization is taken from: + https://en.wikipedia.org/wiki/Extended_Euclidean_algorithm + """ + if a < 0: + # k^-1 = p - (-k)^-1 mod p + return n - modinvert(-a, n) + t, newt = 0, 1 + r, newr = n, a + while newr != 0: + quotinent = r // newr + t, newt = newt, t - quotinent * newt + r, newr = newr, r - quotinent * newr + if r > 1: + return -1 + if t < 0: + t = t + n + return t diff --git a/pygost-5.13/pygost/wrap.py b/pygost-5.13/pygost/wrap.py new file mode 100644 index 0000000..3fa49e7 --- /dev/null +++ b/pygost-5.13/pygost/wrap.py @@ -0,0 +1,152 @@ +# coding: utf-8 +# PyGOST -- Pure Python GOST cryptographic functions library +# Copyright (C) 2015-2023 Sergey Matveev +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3 of the License. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +"""Key wrap. + +:rfc:`4357` key wrapping (28147-89 and CryptoPro). +""" + +from hmac import compare_digest +from struct import pack +from struct import unpack + +from pygost.gost28147 import cfb_encrypt +from pygost.gost28147 import DEFAULT_SBOX +from pygost.gost28147 import ecb_decrypt +from pygost.gost28147 import ecb_encrypt +from pygost.gost28147_mac import MAC +from pygost.gost3413 import ctr +from pygost.gost3413 import mac + + +def wrap_gost(ukm, kek, cek, sbox=DEFAULT_SBOX): + """28147-89 key wrapping + + :param ukm: UKM + :type ukm: bytes, 8 bytes + :param kek: key encryption key + :type kek: bytes, 32 bytes + :param cek: content encryption key + :type cek: bytes, 32 bytes + :returns: wrapped key + :rtype: bytes, 44 bytes + """ + cek_mac = MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4] + cek_enc = ecb_encrypt(kek, cek, sbox=sbox) + return ukm + cek_enc + cek_mac + + +def unwrap_gost(kek, data, sbox=DEFAULT_SBOX): + """28147-89 key unwrapping + + :param kek: key encryption key + :type kek: bytes, 32 bytes + :param data: wrapped key + :type data: bytes, 44 bytes + :returns: unwrapped CEK + :rtype: 32 bytes + """ + if len(data) != 44: + raise ValueError("Invalid data length") + ukm, cek_enc, cek_mac = data[:8], data[8:8 + 32], data[-4:] + cek = ecb_decrypt(kek, cek_enc, sbox=sbox) + if MAC(kek, data=cek, iv=ukm, sbox=sbox).digest()[:4] != cek_mac: + raise ValueError("Invalid MAC") + return cek + + +def wrap_cryptopro(ukm, kek, cek, sbox=DEFAULT_SBOX): + """CryptoPro key wrapping + + :param ukm: UKM + :type ukm: bytes, 8 bytes + :param kek: key encryption key + :type kek: bytes, 32 bytes + :param cek: content encryption key + :type cek: bytes, 32 bytes + :returns: wrapped key + :rtype: bytes, 44 bytes + """ + return wrap_gost( + ukm, + diversify(kek, bytearray(ukm), sbox=sbox), + cek, + sbox=sbox, + ) + + +def unwrap_cryptopro(kek, data, sbox=DEFAULT_SBOX): + """CryptoPro key unwrapping + + :param kek: key encryption key + :type kek: bytes, 32 bytes + :param data: wrapped key + :type data: bytes, 44 bytes + :returns: unwrapped CEK + :rtype: 32 bytes + """ + if len(data) < 8: + raise ValueError("Invalid data length") + return unwrap_gost( + diversify(kek, bytearray(data[:8]), sbox=sbox), + data, + sbox=sbox, + ) + + +def diversify(kek, ukm, sbox=DEFAULT_SBOX): + out = kek + for i in range(8): + s1, s2 = 0, 0 + for j in range(8): + k, = unpack("> j) & 1: + s1 += k + else: + s2 += k + iv = pack("