Contributing ============ Process ------- As an open source project, ``cryptography`` welcomes contributions of all forms. These can include: * Bug reports and feature requests * Pull requests for both code and documentation * Patch reviews You can file bugs and submit pull requests on `GitHub`_. To discuss larger changes you can start a conversation on `our mailing list`_. Because cryptography is so complex, and the implications of getting it wrong so devastating, ``cryptography`` has a strict code review policy: * Patches must *never* be pushed directly to ``master``, all changes (even the most trivial typo fixes!) must be submitted as a pull request. * A committer may *never* merge their own pull request, a second party must merge their changes. If multiple people work on a pull request, it must be merged by someone who did not work on it. * A patch which breaks tests, or introduces regressions by changing or removing existing tests should not be merged. Tests must always be passing on ``master``. * If somehow the tests get into a failing state on ``master`` (such as by a backwards incompatible release of a dependency) no pull requests may be merged until this is rectified. * All merged patches must have 100% test coverage. The purpose of these policies is to minimize the chances we merge a change which jeopardizes our users' security. If you believe you've identified a security issue in ``cryptography``, please follow the directions on the :doc:`security page `. Code ---- When in doubt, refer to `PEP 8`_ for Python code. Every code file must start with the boilerplate notice of the Apache License. Additionally, every Python code file must contain .. code-block:: python from __future__ import absolute_import, division, print_function API Considerations ~~~~~~~~~~~~~~~~~~ Most projects' APIs are designed with a philosophy of "make easy things easy, and make hard things possible". One of the perils of writing cryptographic code is that secure code looks just like insecure code, and its results are almost always indistinguishable. As a result ``cryptography`` has, as a design philosophy: "make it hard to do insecure things". Here are a few strategies for API design which should be both followed, and should inspire other API choices: If it is incorrect to ignore the result of a method, it should raise an exception, and not return a boolean ``True``/``False`` flag. For example, a method to verify a signature should raise ``InvalidSignature``, and not return whether the signature was valid. .. code-block:: python # This is bad. def verify(sig): # ... return is_valid # Good! def verify(sig): # ... if not is_valid: raise InvalidSignature Every recipe should include a version or algorithmic marker of some sort in its output in order to allow transparent upgrading of the algorithms in use, as the algorithms or parameters needed to achieve a given security margin evolve. APIs at the :doc:`/hazmat/primitives/index` layer should always take an explicit backend, APIs at the recipes layer should automatically use the :func:`~cryptography.hazmat.backends.default_backend`, but optionally allow specifying a different backend. C bindings ~~~~~~~~~~ When binding C code with ``cffi`` we have our own style guide, it's pretty simple. Don't name parameters: .. code-block:: c // Good long f(long); // Bad long f(long x); ...unless they're inside a struct: .. code-block:: c struct my_struct { char *name; int number; ...; }; Include ``void`` if the function takes no arguments: .. code-block:: c // Good long f(void); // Bad long f(); Wrap lines at 80 characters like so: .. code-block:: c // Pretend this went to 80 characters long f(long, long, int *) Include a space after commas between parameters: .. code-block:: c // Good long f(int, char *) // Bad long f(int,char *) Values set by ``#define`` should be assigned the appropriate type. If you see this: .. code-block:: c #define SOME_INTEGER_LITERAL 0x0; #define SOME_UNSIGNED_INTEGER_LITERAL 0x0001U; #define SOME_STRING_LITERAL "hello"; ...it should be added to the bindings like so: .. code-block:: c static const int SOME_INTEGER_LITERAL; static const unsigned int SOME_UNSIGNED_INTEGER_LITERAL; static const char *const SOME_STRING_LITERAL; Documentation ------------- All features should be documented with prose. Docstrings should be written like this: .. code-block:: python def some_function(some_arg): """ Does some things. :param some_arg: Some argument. """ So, specifically: * Always use three double quotes. * Put the three double quotes on their own line. * No blank line at the end. * Use Sphinx parameter/attribute documentation `syntax`_. Because of the inherent challenges in implementing correct cryptographic systems, we want to make our documentation point people in the right directions as much as possible. To that end: * When documenting a generic interface, use a strong algorithm in examples. (e.g. when showing a hashing example, don't use :class:`~cryptography.hazmat.primitives.hashes.MD5`) * When giving prescriptive advice, always provide references and supporting material. * When there is real disagreement between cryptographic experts, represent both sides of the argument and describe the trade-offs clearly. When documenting a new module in the ``hazmat`` package, its documentation should begin with the "Hazardous Materials" warning: .. code-block:: rest .. hazmat:: When referring to a hypothetical individual (such as "a person receiving an encrypted message") use gender neutral pronouns (they/them/their). Development Environment ----------------------- Working on ``cryptography`` requires the installation of a small number of development dependencies. These are listed in ``dev-requirements.txt`` and they can be installed in a `virtualenv`_ using `pip`_. Once you've installed the dependencies, install ``cryptography`` in ``editable`` mode. For example: .. code-block:: console $ # Create a virtualenv and activate it $ pip install --requirement dev-requirements.txt $ pip install --editable . You are now ready to run the tests and build the documentation. Running Tests ~~~~~~~~~~~~~ ``cryptography`` unit tests are found in the ``tests/`` directory and are designed to be run using `pytest`_. `pytest`_ will discover the tests automatically, so all you have to do is: .. code-block:: console $ py.test ... 62746 passed in 220.43 seconds This runs the tests with the default Python interpreter. You can also verify that the tests pass on other supported Python interpreters. For this we use `tox`_, which will automatically create a `virtualenv`_ for each supported Python version and run the tests. For example: .. code-block:: console $ tox ... ERROR: py26: InterpreterNotFound: python2.6 py27: commands succeeded ERROR: pypy: InterpreterNotFound: pypy ERROR: py32: InterpreterNotFound: python3.2 py33: commands succeeded docs: commands succeeded pep8: commands succeeded You may not have all the required Python versions installed, in which case you will see one or more ``InterpreterNotFound`` errors. Building Documentation ~~~~~~~~~~~~~~~~~~~~~~ ``cryptography`` documentation is stored in the ``docs/`` directory. It is written in `reStructured Text`_ and rendered using `Sphinx`_. Use `tox`_ to build the documentation. For example: .. code-block:: console $ tox -e docs ... docs: commands succeeded congratulations :) The HTML documentation index can now be found at ``docs/_build/html/index.html``. .. _`GitHub`: https://github.com/pyca/cryptography .. _`our mailing list`: https://mail.python.org/mailman/listinfo/cryptography-dev .. _`PEP 8`: http://www.peps.io/8/ .. _`syntax`: http://sphinx-doc.org/domains.html#info-field-lists .. _`pytest`: https://pypi.python.org/pypi/pytest .. _`tox`: https://pypi.python.org/pypi/tox .. _`virtualenv`: https://pypi.python.org/pypi/virtualenv .. _`pip`: https://pypi.python.org/pypi/pip .. _`sphinx`: https://pypi.python.org/pypi/sphinx .. _`reStructured Text`: http://sphinx-doc.org/rest.html > 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382
--  File operations for interpreter
--  Copyright (C) 2014 Tristan Gingold
--
--  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 2 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 <gnu.org/licenses>.

with Types; use Types;
with Vhdl.Annotations; use Vhdl.Annotations;
with Simul.Execution; use Simul.Execution;
with Simul.Debugger; use Simul.Debugger;
with Simul.Grt_Interface; use Simul.Grt_Interface;
with Grt.Lib;

package body Simul.File_Operation is
   --  Open a file.
   --  See LRM93 3.4.1 for definition of arguments.
   --  IS_TEXT is true if the file format is text.
   --  The purpose of the IS_TEXT is to allow a text implementation of file
   --  type TEXT, defined in std.textio.
   procedure File_Open (Status : out Ghdl_I32;
                        File : Iir_Value_Literal_Acc;
                        External_Name : Iir_Value_Literal_Acc;
                        Mode : Ghdl_I32;
                        Is_Text : Boolean;
                        Return_Status : Boolean)
   is
      Name_Len : constant Ghdl_Index_Type :=
        Ghdl_Index_Type (External_Name.Bounds.D (1).Length);
      Name_Str : aliased Std_String_Uncons (1 .. Name_Len);
      Name_Bnd : aliased Std_String_Bound := Build_Bound (External_Name);
      Name : aliased Std_String := (To_Std_String_Basep (Name_Str'Address),
                                    To_Std_String_Boundp (Name_Bnd'Address));
   begin
      -- Convert the string to an Ada string.
      for I in External_Name.Val_Array.V'Range loop
         Name_Str (Name_Str'First + Ghdl_Index_Type (I - 1)) :=
           Character'Val (External_Name.Val_Array.V (I).E8);
      end loop;

      if Is_Text then
         if Return_Status then
            Status := Ghdl_Text_File_Open_Status
              (File.File, Mode, Name'Unrestricted_Access);
         else
            Ghdl_Text_File_Open (File.File, Mode, Name'Unrestricted_Access);
            Status := Open_Ok;
         end if;
      else
         if Return_Status then
            Status := Ghdl_File_Open_Status
              (File.File, Mode, Name'Unrestricted_Access);
         else
            Ghdl_File_Open (File.File, Mode, Name'Unrestricted_Access);
            Status := Open_Ok;
         end if;
      end if;
   end File_Open;

   --  Open a file.
   procedure File_Open (File : Iir_Value_Literal_Acc;
                        Name : Iir_Value_Literal_Acc;
                        Mode : Iir_Value_Literal_Acc;
                        File_Decl : Iir;
                        Stmt : Iir)
   is
      pragma Unreferenced (Stmt);
      Is_Text : constant Boolean := Get_Text_File_Flag (Get_Type (File_Decl));
      File_Mode : constant Ghdl_I32 := Ghdl_I32 (Mode.E8);
      Status : Ghdl_I32;
   begin
      File_Open (Status, File, Name, File_Mode, Is_Text, False);
      if Status /= Open_Ok then
         raise Program_Error;
      end if;
   end File_Open;

   procedure File_Open_Status (Status : Iir_Value_Literal_Acc;
                               File : Iir_Value_Literal_Acc;
                               Name : Iir_Value_Literal_Acc;
                               Mode : Iir_Value_Literal_Acc;
                               File_Decl : Iir;
                               Stmt : Iir)
   is
      pragma Unreferenced (Stmt);
      Is_Text : constant Boolean := Get_Text_File_Flag (Get_Type (File_Decl));
      File_Mode : constant Ghdl_I32 := Ghdl_I32 (Mode.E8);
      R_Status : Ghdl_I32;
   begin
      File_Open (R_Status, File, Name, File_Mode, Is_Text, True);
      Status.E8 := Ghdl_E8 (R_Status);
   end File_Open_Status;

   function Elaborate_File_Declaration
     (Instance: Block_Instance_Acc; Decl: Iir_File_Declaration)
     return Iir_Value_Literal_Acc
   is
      Def : constant Iir := Get_Type (Decl);
      External_Name : Iir;
      File_Name: Iir_Value_Literal_Acc;
      Is_Text : constant Boolean := Get_Text_File_Flag (Def);
      File_Mode : Ghdl_I32;
      Res : Iir_Value_Literal_Acc;
      Status : Ghdl_I32;
      Mode : Iir_Value_Literal_Acc;
   begin
      if Is_Text then
         Res := Create_File_Value (Ghdl_Text_File_Elaborate);
      else
         declare
            Sig : constant String_Acc := Get_Info (Def).File_Signature;
            Cstr : Ghdl_C_String;
         begin
            if Sig = null then
               Cstr := null;
            else
               Cstr := To_Ghdl_C_String (Sig.all'Address);
            end if;
            Res := Create_File_Value (Ghdl_File_Elaborate (Cstr));
         end;
      end if;

      External_Name := Get_File_Logical_Name (Decl);

      --  LRM93 4.3.1.4
      --  If file open information is not included in a given file declaration,
      --  then the file declared by the declaration is not opened when the file
      --  declaration is elaborated.
      if External_Name = Null_Iir then
         return Res;
      end if;

      File_Name := Execute_Expression (Instance, External_Name);
      if Get_File_Open_Kind (Decl) /= Null_Iir then
         Mode := Execute_Expression (Instance, Get_File_Open_Kind (Decl));
         File_Mode := Ghdl_I32 (Mode.E8);
      else
         case Get_Mode (Decl) is
            when Iir_In_Mode =>
               File_Mode := Read_Mode;
            when Iir_Out_Mode =>
               File_Mode := Write_Mode;
            when others =>
               raise Internal_Error;
         end case;
      end if;
      File_Open (Status, Res, File_Name, File_Mode, Is_Text, False);
      return Res;
   end Elaborate_File_Declaration;

   procedure File_Close_Text (File : Iir_Value_Literal_Acc; Stmt : Iir) is
      pragma Unreferenced (Stmt);
   begin
      Ghdl_Text_File_Close (File.File);
   end File_Close_Text;

   procedure File_Close_Binary (File : Iir_Value_Literal_Acc; Stmt : Iir) is
      pragma Unreferenced (Stmt);
   begin
      Ghdl_File_Close (File.File);
   end File_Close_Binary;

   procedure File_Destroy_Text (File : Iir_Value_Literal_Acc) is
   begin
      Ghdl_Text_File_Finalize (File.File);
   end File_Destroy_Text;

   procedure File_Destroy_Binary (File : Iir_Value_Literal_Acc) is
   begin
      Ghdl_File_Finalize (File.File);
   end File_Destroy_Binary;


   procedure Write_Binary (File: Iir_Value_Literal_Acc;
                           Value: Iir_Value_Literal_Acc) is
   begin
      case Value.Kind is
         when Iir_Value_B1 =>
            Ghdl_Write_Scalar (File.File, Ghdl_Ptr (Value.B1'Address), 1);
         when Iir_Value_I64 =>
            Ghdl_Write_Scalar (File.File, Ghdl_Ptr (Value.I64'Address), 8);
         when Iir_Value_E8 =>
            Ghdl_Write_Scalar (File.File, Ghdl_Ptr (Value.E8'Address), 1);
         when Iir_Value_E32 =>
            Ghdl_Write_Scalar (File.File, Ghdl_Ptr (Value.E32'Address), 4);
         when Iir_Value_F64 =>
            Ghdl_Write_Scalar (File.File, Ghdl_Ptr (Value.F64'Address), 8);
         when Iir_Value_Array =>
            for I in Value.Bounds.D'Range loop
               Ghdl_Write_Scalar
                 (File.File, Ghdl_Ptr (Value.Bounds.D (I).Length'Address), 4);
            end loop;
            for I in Value.Val_Array.V'Range loop
               Write_Binary (File, Value.Val_Array.V (I));
            end loop;
         when Iir_Value_Record =>
            for I in Value.Val_Record.V'Range loop
               Write_Binary (File, Value.Val_Record.V (I));
            end loop;
         when others =>
            raise Internal_Error;
      end case;
   end Write_Binary;

   procedure Write_Text (File: Iir_Value_Literal_Acc;
                         Value: Iir_Value_Literal_Acc)
   is
      Val_Len : constant Ghdl_Index_Type :=
        Ghdl_Index_Type (Value.Bounds.D (1).Length);
      Val_Str : aliased Std_String_Uncons (1 .. Val_Len);
      Val_Bnd : aliased Std_String_Bound := Build_Bound (Value);
      Val : aliased Std_String := (To_Std_String_Basep (Val_Str'Address),
                                    To_Std_String_Boundp (Val_Bnd'Address));
   begin
      -- Convert the string to an Ada string.
      for I in Value.Val_Array.V'Range loop
         Val_Str (Val_Str'First + Ghdl_Index_Type (I - 1)) :=
           Character'Val (Value.Val_Array.V (I).E8);
      end loop;

      Ghdl_Text_Write (File.File, Val'Unrestricted_Access);
   end Write_Text;

   function Endfile (File : Iir_Value_Literal_Acc; Stmt : Iir)
                    return Boolean
   is
      pragma Unreferenced (Stmt);
   begin
      return Grt.Files.Ghdl_File_Endfile (File.File);
   end Endfile;

   procedure Read_Length_Text (File : Iir_Value_Literal_Acc;
                               Value : Iir_Value_Literal_Acc;
                               Length : Iir_Value_Literal_Acc)
   is
      Val_Len : constant Ghdl_Index_Type :=
        Ghdl_Index_Type (Value.Bounds.D (1).Length);
      Val_Str : aliased Std_String_Uncons (1 .. Val_Len);
      Val_Bnd : aliased Std_String_Bound := Build_Bound (Value);
      Val : aliased Std_String := (To_Std_String_Basep (Val_Str'Address),
                                   To_Std_String_Boundp (Val_Bnd'Address));
      Len : Std_Integer;
   begin
      Len := Ghdl_Text_Read_Length (File.File, Val'Unrestricted_Access);
      for I in 1 .. Len loop
         Value.Val_Array.V (Iir_Index32 (I)).E8 :=
           Character'Pos (Val_Str (Ghdl_Index_Type (I)));
      end loop;
      Length.I64 := Ghdl_I64 (Len);
   end Read_Length_Text;

   procedure Untruncated_Text_Read (File : Iir_Value_Literal_Acc;
                                    Str : Iir_Value_Literal_Acc;
                                    Length : Iir_Value_Literal_Acc)
   is
      Len : Std_Integer;
      Val_Len : constant Ghdl_Index_Type :=
        Ghdl_Index_Type (Str.Bounds.D (1).Length);
      Val_Str : aliased Std_String_Uncons (1 .. Val_Len);
      Val_Bnd : aliased Std_String_Bound := Build_Bound (Str);
      Val : aliased Std_String := (To_Std_String_Basep (Val_Str'Address),
                                   To_Std_String_Boundp (Val_Bnd'Address));
   begin
      Ghdl_Untruncated_Text_Read
        (File.File, Val'Unrestricted_Access, Len'Unrestricted_Access);
      for I in 1 .. Len loop
         Str.Val_Array.V (Iir_Index32 (I)).E8 :=
           Character'Pos (Val_Str (Ghdl_Index_Type (I)));
      end loop;
      Length.I64 := Ghdl_I64 (Len);
   end Untruncated_Text_Read;

   procedure Read_Binary (File: Iir_Value_Literal_Acc;
                          Value: Iir_Value_Literal_Acc)
   is
   begin
      case Value.Kind is
         when Iir_Value_B1 =>
            Ghdl_Read_Scalar (File.File, Ghdl_Ptr (Value.B1'Address), 1);
         when Iir_Value_I64 =>
            Ghdl_Read_Scalar (File.File, Ghdl_Ptr (Value.I64'Address), 8);
         when Iir_Value_E8 =>
            Ghdl_Read_Scalar (File.File, Ghdl_Ptr (Value.E8'Address), 1);
         when Iir_Value_E32 =>
            Ghdl_Read_Scalar (File.File, Ghdl_Ptr (Value.E32'Address), 4);
         when Iir_Value_F64 =>
            Ghdl_Read_Scalar (File.File, Ghdl_Ptr (Value.F64'Address), 8);
         when Iir_Value_Array =>
            for I in Value.Bounds.D'Range loop
               declare
                  Len : Iir_Index32;
               begin
                  Ghdl_Read_Scalar (File.File, Ghdl_Ptr (Len'Address), 4);
                  if Len /= Value.Bounds.D (I).Length then
                     Error_Msg_Constraint (Null_Iir); --  FIXME: loc
                  end if;
               end;
            end loop;
            for I in Value.Val_Array.V'Range loop
               Read_Binary (File, Value.Val_Array.V (I));
            end loop;
         when Iir_Value_Record =>
            for I in Value.Val_Record.V'Range loop
               Read_Binary (File, Value.Val_Record.V (I));
            end loop;
         when others =>
            raise Internal_Error;
      end case;
   end Read_Binary;

   procedure Read_Length_Binary (File : Iir_Value_Literal_Acc;
                                 Value : Iir_Value_Literal_Acc;
                                 Length : Iir_Value_Literal_Acc)
   is
      Len : Iir_Index32;
   begin
      Ghdl_Read_Scalar (File.File, Ghdl_Ptr (Len'Address), 4);
      for I in 1 .. Len loop
         if I <= Value.Bounds.D (1).Length then
            Read_Binary (File, Value.Val_Array.V (I));
         else
            --  FIXME: for empty arrays ??
            --  Lose_Binary (File, Value.Val_Array (0));
            raise Internal_Error;
         end if;
      end loop;
      Length.I64 := Ghdl_I64 (Len);
   end Read_Length_Binary;

   procedure Flush (File : Iir_Value_Literal_Acc) is
   begin
      Ghdl_File_Flush (File.File);
   end Flush;

   procedure Textio_Write_Real (Str : Iir_Value_Literal_Acc;
                                Len : Iir_Value_Literal_Acc;
                                Val : Ghdl_F64;
                                Ndigits : Std_Integer)
   is
      Len_Arg : aliased Std_Integer;
      Str_Len : constant Ghdl_Index_Type :=
        Ghdl_Index_Type (Str.Bounds.D (1).Length);
      Str_Str : aliased Std_String_Uncons (1 .. Str_Len);
      Str_Bnd : aliased Std_String_Bound := Build_Bound (Str);
      Str_Arg : aliased Std_String := (To_Std_String_Basep (Str_Str'Address),
                                       To_Std_String_Boundp (Str_Bnd'Address));
   begin
      Grt.Lib.Textio_Write_Real
        (Str_Arg'Unrestricted_Access, Len_Arg'Unrestricted_Access,
         Val, Ndigits);
      for I in 1 .. Len_Arg loop
         Str.Val_Array.V (Iir_Index32 (I)).E8 :=
           Character'Pos (Str_Str (Ghdl_Index_Type (I)));
      end loop;
      Len.I64 := Ghdl_I64 (Len_Arg);
   end Textio_Write_Real;

   function Textio_Read_Real (Str : Iir_Value_Literal_Acc) return Ghdl_F64
   is
      Str_Len : constant Ghdl_Index_Type :=
        Ghdl_Index_Type (Str.Bounds.D (1).Length);
      Str_Str : aliased Std_String_Uncons (1 .. Str_Len);
      Str_Bnd : aliased Std_String_Bound := Build_Bound (Str);
      Str_Arg : aliased Std_String := (To_Std_String_Basep (Str_Str'Address),
                                       To_Std_String_Boundp (Str_Bnd'Address));
   begin
      for I in Str.Val_Array.V'Range loop
         Str_Str (Ghdl_Index_Type (I)) :=
           Character'Val (Str.Val_Array.V (I).E8);
      end loop;
      return Grt.Lib.Textio_Read_Real (Str_Arg'Unrestricted_Access);
   end Textio_Read_Real;
end Simul.File_Operation;