--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ END OF TERMS AND CONDITIONS
+\f
+ 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
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 675 Mass Ave, Cambridge, MA 02139, USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) 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
+this service 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 make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. 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.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute 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 and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+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
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the 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 a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, 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.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE 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.
+
+ END OF TERMS AND CONDITIONS
+\f
+ Appendix: 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
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ 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, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision 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, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This 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 Library General
+Public License instead of this License.
--- /dev/null
+- created
+
+- released 0.0.1
+
+- simplified s, A, B
+
+- released 0.0.2
+
+- simplified nxt=
+
+- released 0.0.3
+
+- removed commented code
+- code formatting
+
+- released 0.0.4
+
+- kk now unsigned char
+- removed 64 bit ints
+
+- released 0.0.5
+
+- created decrypt_2ts
+
+- released 0.0.6
+
+- renamed files
+- created decrypt_many_ts, removed others
+- external interface has 2 functions only: set_cws() and decrypt_many_ts()
+- reformatted code
+- reimplemented s12,s34,s56,s7
+- unsigned char become int for table optimization
+
+- released 0.0.7
+
+- optional icc compiler
+- kk now 0..55
+- decrypt_many_ts really works (no parallelism yet)
+- added get_cws() to interface
+- created stream.c
+- created key_schedule_stream, using iA[] and iB[]
+
+- released 0.0.8
+
+- decrypt_many_ts() makes a group, sorts the packets, processes them
+- preliminar stream_cypher_group() created
+- parallel computing activated
+- huge speed increase (+500%) thanks to stream_cypher_group()
+
+- released 0.0.9
+
+- block_cypher_group() created (no parallelism yet)
+
+- released 0.0.10
+
+- block_cypher_group() has 56 simple iterations
+- block_cypher_group() doesn't shift registers anymore
+
+- released 0.0.11
+
+- some parallelization on block_cypher_group()
+
+- released 0.0.12
+
+- better parallelization of block_cypher_group()
+
+- released 0.0.13
+
+- block_cypher() was still called by error when N=23
+- speed is now 109Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.14
+
+- stream_cypher_group() has a init and normal variant
+- A[0]-A[9] instead of A[1]-A[10], same for B
+- implemented virtual shift of A and B
+- speed is now 117Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.15
+
+- better optimization of E and F in the stream cypher
+- speed is now 119Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.16
+
+- removed some debug overhead
+- speed is now 120Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.17
+
+- don't move packets with residue anymore
+- speed is now 123Mbit/s on AMD XP2000+ CPU
+
+- released 0.0.18
+
+- solved alignment problems
+- search groupable packets even beyond ungroupable ones
+ (more speed in some real world cases)
+- created decrypt_many_ts2(), useful with circular buffers
+
+- released 0.0.19
+
+- removed old code
+
+- released 0.0.20
+
+- partially converted code to size-independent group
+- icc doesn't work with optimizations on
+
+- released 0.1.1
+
+- merge loops on block_decypher (speed++ gcc, speed-- icc)
+- transposition are now functions (speed-- icc)
+- icc works again (compiler bug work around?)
+
+- released 0.1.2
+
+- better use of COPY8 &co
+- better flags for gcc
+- removed old code
+
+- released 0.1.3
+
+- int and not char in block cypher (speed++++++ gcc, speed-- icc)
+
+- released 0.1.4
+
+- group abstraction finally implemented
+- support for group width 64
+
+- released 0.1.5
+
+- group 64 mmx implemented (speed++ gcc)
+
+- released 0.1.6
+
+- more parallelism in block cypher (speed++ gcc)
+- transposition before and after block (disabled because of no speed gain yet)
+
+- released 0.1.7
+
+- more parallelism in block cypher (speed++ gcc)
+- transposition before and after block enabled (speed++ gcc)
+- gcc options (unrolled 500) speed gcc++
+
+- released 0.1.8
+
+- reworked FFN_ALL_* constants (speed++++ gcc)
+
+- released 0.1.9
+
+- transposition in block as inlined functions
+- group abstraction working well
+
+- released 0.1.10
+
+- group 128 sse implemented, but batch is 64 mmx (not faster than group 64 mmx)
+
+- released 0.1.11
+
+- lot of code polishing and dead code elimination
+- better and more debug output
+
+- released 0.1.12
+
+- name change: FFdecsa
+
+- released 0.2.0
+
+- separated test cases
+- corrected all group_modes (now called parallel_modes)
+- parallel 128 8 char implemented
+- parallel 64 long implemented
+- parallel 128 2 long implemented
+- parallel 128 2 mmx implemented (incredibly slow, the compiler is very confused)
+- parallel 128 16 charA implemented (very slow compilation)
+- parallel 128 16 char implemented
+- renamed softcsa* to FFdecsa*
+
+- released 0.2.1
+
+- new external interface (based on ranges)
+
+- released 0.2.2
+
+- can be compiled with g++ too
+- using g++ the code is 3% faster!
+- external interface: function name changing and new functions
+- a group of ranges is now called a cluster
+- renamed autogenerated files
+
+- released 0.2.3
+
+- written docs
+- removed unneeded files
+- added Copyright and license notes
+- reworked "logic"
+
+- released 0.3.0
+
+- Makefile reworked
+- misc fixes
+- added vdr patch
+
+- released 1.0.0 (public release)
+
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <sys/types.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "FFdecsa.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+//#define DEBUG
+#ifdef DEBUG
+#define DBG(a) a
+#else
+#define DBG(a)
+#endif
+
+//// parallelization stuff, large speed differences are possible
+// possible choices
+#define PARALLEL_32_4CHAR 320
+#define PARALLEL_32_4CHARA 321
+#define PARALLEL_32_INT 322
+#define PARALLEL_64_8CHAR 640
+#define PARALLEL_64_8CHARA 641
+#define PARALLEL_64_2INT 642
+#define PARALLEL_64_LONG 643
+#define PARALLEL_64_MMX 644
+#define PARALLEL_128_16CHAR 1280
+#define PARALLEL_128_16CHARA 1281
+#define PARALLEL_128_4INT 1282
+#define PARALLEL_128_2LONG 1283
+#define PARALLEL_128_2MMX 1284
+#define PARALLEL_128_SSE 1285
+#define PARALLEL_128_SSE2 1286
+
+//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
+#ifndef PARALLEL_MODE
+#define PARALLEL_MODE PARALLEL_32_INT
+#endif
+//////// our choice //////////////// our choice //////////////// our choice //////////////// our choice ////////
+
+#include "parallel_generic.h"
+//// conditionals
+#if PARALLEL_MODE==PARALLEL_32_4CHAR
+#include "parallel_032_4char.h"
+#elif PARALLEL_MODE==PARALLEL_32_4CHARA
+#include "parallel_032_4charA.h"
+#elif PARALLEL_MODE==PARALLEL_32_INT
+#include "parallel_032_int.h"
+#elif PARALLEL_MODE==PARALLEL_64_8CHAR
+#include "parallel_064_8char.h"
+#elif PARALLEL_MODE==PARALLEL_64_8CHARA
+#include "parallel_064_8charA.h"
+#elif PARALLEL_MODE==PARALLEL_64_2INT
+#include "parallel_064_2int.h"
+#elif PARALLEL_MODE==PARALLEL_64_LONG
+#include "parallel_064_long.h"
+#elif PARALLEL_MODE==PARALLEL_64_MMX
+#include "parallel_064_mmx.h"
+#elif PARALLEL_MODE==PARALLEL_128_16CHAR
+#include "parallel_128_16char.h"
+#elif PARALLEL_MODE==PARALLEL_128_16CHARA
+#include "parallel_128_16charA.h"
+#elif PARALLEL_MODE==PARALLEL_128_4INT
+#include "parallel_128_4int.h"
+#elif PARALLEL_MODE==PARALLEL_128_2LONG
+#include "parallel_128_2long.h"
+#elif PARALLEL_MODE==PARALLEL_128_2MMX
+#include "parallel_128_2mmx.h"
+#elif PARALLEL_MODE==PARALLEL_128_SSE
+#include "parallel_128_sse.h"
+#elif PARALLEL_MODE==PARALLEL_128_SSE2
+#include "parallel_128_sse2.h"
+#else
+#error "unknown/undefined parallel mode"
+#endif
+
+// stuff depending on conditionals
+
+#define BYTES_PER_GROUP (GROUP_PARALLELISM/8)
+#define BYPG BYTES_PER_GROUP
+#define BITS_PER_GROUP GROUP_PARALLELISM
+#define BIPG BITS_PER_GROUP
+
+#ifndef MALLOC
+#define MALLOC(X) malloc(X)
+#endif
+#ifndef FREE
+#define FREE(X) free(X)
+#endif
+#ifndef MEMALIGN
+#define MEMALIGN
+#endif
+
+//// debug tool
+
+static void dump_mem(const char *string, const unsigned char *p, int len, int linelen){
+ int i;
+ for(i=0;i<len;i++){
+ if(i%linelen==0&&i) fprintf(stderr,"\n");
+ if(i%linelen==0) fprintf(stderr,"%s %08x:",string,i);
+ else{
+ if(i%8==0) fprintf(stderr," ");
+ if(i%4==0) fprintf(stderr," ");
+ }
+ fprintf(stderr," %02x",p[i]);
+ }
+ if(i%linelen==0) fprintf(stderr,"\n");
+}
+
+//////////////////////////////////////////////////////////////////////////////////
+
+struct csa_key_t{
+ unsigned char ck[8];
+// used by stream
+ int iA[8]; // iA[0] is for A1, iA[7] is for A8
+ int iB[8]; // iB[0] is for B1, iB[7] is for B8
+// used by stream (group)
+ MEMALIGN group ck_g[8][8]; // [byte][bit:0=LSB,7=MSB]
+ MEMALIGN group iA_g[8][4]; // [0 for A1][0 for LSB]
+ MEMALIGN group iB_g[8][4]; // [0 for B1][0 for LSB]
+// used by block
+ unsigned char kk[56];
+// used by block (group)
+ MEMALIGN batch kkmulti[56]; // many times the same byte in every batch
+};
+
+struct csa_keys_t{
+ struct csa_key_t even;
+ struct csa_key_t odd;
+};
+
+//-----stream cypher
+
+//-----key schedule for stream decypher
+static void key_schedule_stream(
+ unsigned char *ck, // [In] ck[0]-ck[7] 8 bytes | Key.
+ int *iA, // [Out] iA[0]-iA[7] 8 nibbles | Key schedule.
+ int *iB) // [Out] iB[0]-iB[7] 8 nibbles | Key schedule.
+{
+ iA[0]=(ck[0]>>4)&0xf;
+ iA[1]=(ck[0] )&0xf;
+ iA[2]=(ck[1]>>4)&0xf;
+ iA[3]=(ck[1] )&0xf;
+ iA[4]=(ck[2]>>4)&0xf;
+ iA[5]=(ck[2] )&0xf;
+ iA[6]=(ck[3]>>4)&0xf;
+ iA[7]=(ck[3] )&0xf;
+ iB[0]=(ck[4]>>4)&0xf;
+ iB[1]=(ck[4] )&0xf;
+ iB[2]=(ck[5]>>4)&0xf;
+ iB[3]=(ck[5] )&0xf;
+ iB[4]=(ck[6]>>4)&0xf;
+ iB[5]=(ck[6] )&0xf;
+ iB[6]=(ck[7]>>4)&0xf;
+ iB[7]=(ck[7] )&0xf;
+}
+
+//----- stream main function
+
+#define STREAM_INIT
+#include "stream.c"
+#undef STREAM_INIT
+
+#define STREAM_NORMAL
+#include "stream.c"
+#undef STREAM_NORMAL
+
+
+//-----block decypher
+
+//-----key schedule for block decypher
+
+static void key_schedule_block(
+ unsigned char *ck, // [In] ck[0]-ck[7] 8 bytes | Key.
+ unsigned char *kk) // [Out] kk[0]-kk[55] 56 bytes | Key schedule.
+{
+ static const unsigned char key_perm[0x40] = {
+ 0x12,0x24,0x09,0x07,0x2A,0x31,0x1D,0x15, 0x1C,0x36,0x3E,0x32,0x13,0x21,0x3B,0x40,
+ 0x18,0x14,0x25,0x27,0x02,0x35,0x1B,0x01, 0x22,0x04,0x0D,0x0E,0x39,0x28,0x1A,0x29,
+ 0x33,0x23,0x34,0x0C,0x16,0x30,0x1E,0x3A, 0x2D,0x1F,0x08,0x19,0x17,0x2F,0x3D,0x11,
+ 0x3C,0x05,0x38,0x2B,0x0B,0x06,0x0A,0x2C, 0x20,0x3F,0x2E,0x0F,0x03,0x26,0x10,0x37,
+ };
+
+ int i,j,k;
+ int bit[64];
+ int newbit[64];
+ int kb[7][8];
+
+ // 56 steps
+ // 56 key bytes kk(55)..kk(0) by key schedule from ck
+
+ // kb(6,0) .. kb(6,7) = ck(0) .. ck(7)
+ kb[6][0] = ck[0];
+ kb[6][1] = ck[1];
+ kb[6][2] = ck[2];
+ kb[6][3] = ck[3];
+ kb[6][4] = ck[4];
+ kb[6][5] = ck[5];
+ kb[6][6] = ck[6];
+ kb[6][7] = ck[7];
+
+ // calculate kb[5] .. kb[0]
+ for(i=5; i>=0; i--){
+ // 64 bit perm on kb
+ for(j=0; j<8; j++){
+ for(k=0; k<8; k++){
+ bit[j*8+k] = (kb[i+1][j] >> (7-k)) & 1;
+ newbit[key_perm[j*8+k]-1] = bit[j*8+k];
+ }
+ }
+ for(j=0; j<8; j++){
+ kb[i][j] = 0;
+ for(k=0; k<8; k++){
+ kb[i][j] |= newbit[j*8+k] << (7-k);
+ }
+ }
+ }
+
+ // xor to give kk
+ for(i=0; i<7; i++){
+ for(j=0; j<8; j++){
+ kk[i*8+j] = kb[i][j] ^ i;
+ }
+ }
+
+}
+
+//-----block utils
+
+static inline __attribute__((always_inline)) void trasp_N_8 (unsigned char *in,unsigned char* out,int count){
+ int *ri=(int *)in;
+ int *ibi=(int *)out;
+ int j,i,k,g;
+ // copy and first step
+ for(g=0;g<count;g++){
+ ri[g]=ibi[2*g];
+ ri[GROUP_PARALLELISM+g]=ibi[2*g+1];
+ }
+//dump_mem("NE1 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01230123
+#define INTS_PER_ROW (GROUP_PARALLELISM/8*2)
+ for(j=0;j<8;j+=4){
+ for(i=0;i<2;i++){
+ for(k=0;k<INTS_PER_ROW;k++){
+ unsigned int t,b;
+ t=ri[INTS_PER_ROW*(j+i)+k];
+ b=ri[INTS_PER_ROW*(j+i+2)+k];
+ ri[INTS_PER_ROW*(j+i)+k]= (t&0x0000ffff) | ((b )<<16);
+ ri[INTS_PER_ROW*(j+i+2)+k]= ((t )>>16) | (b&0xffff0000) ;
+ }
+ }
+ }
+//dump_mem("NE2 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01010101
+ for(j=0;j<8;j+=2){
+ for(i=0;i<1;i++){
+ for(k=0;k<INTS_PER_ROW;k++){
+ unsigned int t,b;
+ t=ri[INTS_PER_ROW*(j+i)+k];
+ b=ri[INTS_PER_ROW*(j+i+1)+k];
+ ri[INTS_PER_ROW*(j+i)+k]= (t&0x00ff00ff) | ((b&0x00ff00ff)<<8);
+ ri[INTS_PER_ROW*(j+i+1)+k]= ((t&0xff00ff00)>>8) | (b&0xff00ff00);
+ }
+ }
+ }
+//dump_mem("NE3 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 00000000
+}
+
+static inline __attribute__((always_inline)) void trasp_8_N (unsigned char *in,unsigned char* out,int count){
+ int *ri=(int *)in;
+ int *bdi=(int *)out;
+ int j,i,k,g;
+#define INTS_PER_ROW (GROUP_PARALLELISM/8*2)
+//dump_mem("NE1 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 00000000
+ for(j=0;j<8;j+=2){
+ for(i=0;i<1;i++){
+ for(k=0;k<INTS_PER_ROW;k++){
+ unsigned int t,b;
+ t=ri[INTS_PER_ROW*(j+i)+k];
+ b=ri[INTS_PER_ROW*(j+i+1)+k];
+ ri[INTS_PER_ROW*(j+i)+k]= (t&0x00ff00ff) | ((b&0x00ff00ff)<<8);
+ ri[INTS_PER_ROW*(j+i+1)+k]= ((t&0xff00ff00)>>8) | (b&0xff00ff00);
+ }
+ }
+ }
+//dump_mem("NE2 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01010101
+ for(j=0;j<8;j+=4){
+ for(i=0;i<2;i++){
+ for(k=0;k<INTS_PER_ROW;k++){
+ unsigned int t,b;
+ t=ri[INTS_PER_ROW*(j+i)+k];
+ b=ri[INTS_PER_ROW*(j+i+2)+k];
+ ri[INTS_PER_ROW*(j+i)+k]= (t&0x0000ffff) | ((b )<<16);
+ ri[INTS_PER_ROW*(j+i+2)+k]= ((t )>>16) | (b&0xffff0000) ;
+ }
+ }
+ }
+//dump_mem("NE3 r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+// now 01230123
+ for(g=0;g<count;g++){
+ bdi[2*g]=ri[g];
+ bdi[2*g+1]=ri[GROUP_PARALLELISM+g];
+ }
+}
+
+//-----block main function
+
+// block group
+static void block_decypher_group(
+ batch *kkmulti, // [In] kkmulti[0]-kkmulti[55] 56 batches | Key schedule (each batch has repeated equal bytes).
+ unsigned char *ib, // [In] (ib0,ib1,...ib7)...x32 32*8 bytes | Initialization vector.
+ unsigned char *bd, // [Out] (bd0,bd1,...bd7)...x32 32*8 bytes | Block decipher.
+ int count)
+{
+ // int is faster than unsigned char. apparently not
+ static const unsigned char block_sbox[0x100] = {
+ 0x3A,0xEA,0x68,0xFE,0x33,0xE9,0x88,0x1A, 0x83,0xCF,0xE1,0x7F,0xBA,0xE2,0x38,0x12,
+ 0xE8,0x27,0x61,0x95,0x0C,0x36,0xE5,0x70, 0xA2,0x06,0x82,0x7C,0x17,0xA3,0x26,0x49,
+ 0xBE,0x7A,0x6D,0x47,0xC1,0x51,0x8F,0xF3, 0xCC,0x5B,0x67,0xBD,0xCD,0x18,0x08,0xC9,
+ 0xFF,0x69,0xEF,0x03,0x4E,0x48,0x4A,0x84, 0x3F,0xB4,0x10,0x04,0xDC,0xF5,0x5C,0xC6,
+ 0x16,0xAB,0xAC,0x4C,0xF1,0x6A,0x2F,0x3C, 0x3B,0xD4,0xD5,0x94,0xD0,0xC4,0x63,0x62,
+ 0x71,0xA1,0xF9,0x4F,0x2E,0xAA,0xC5,0x56, 0xE3,0x39,0x93,0xCE,0x65,0x64,0xE4,0x58,
+ 0x6C,0x19,0x42,0x79,0xDD,0xEE,0x96,0xF6, 0x8A,0xEC,0x1E,0x85,0x53,0x45,0xDE,0xBB,
+ 0x7E,0x0A,0x9A,0x13,0x2A,0x9D,0xC2,0x5E, 0x5A,0x1F,0x32,0x35,0x9C,0xA8,0x73,0x30,
+
+ 0x29,0x3D,0xE7,0x92,0x87,0x1B,0x2B,0x4B, 0xA5,0x57,0x97,0x40,0x15,0xE6,0xBC,0x0E,
+ 0xEB,0xC3,0x34,0x2D,0xB8,0x44,0x25,0xA4, 0x1C,0xC7,0x23,0xED,0x90,0x6E,0x50,0x00,
+ 0x99,0x9E,0x4D,0xD9,0xDA,0x8D,0x6F,0x5F, 0x3E,0xD7,0x21,0x74,0x86,0xDF,0x6B,0x05,
+ 0x8E,0x5D,0x37,0x11,0xD2,0x28,0x75,0xD6, 0xA7,0x77,0x24,0xBF,0xF0,0xB0,0x02,0xB7,
+ 0xF8,0xFC,0x81,0x09,0xB1,0x01,0x76,0x91, 0x7D,0x0F,0xC8,0xA0,0xF2,0xCB,0x78,0x60,
+ 0xD1,0xF7,0xE0,0xB5,0x98,0x22,0xB3,0x20, 0x1D,0xA6,0xDB,0x7B,0x59,0x9F,0xAE,0x31,
+ 0xFB,0xD3,0xB6,0xCA,0x43,0x72,0x07,0xF4, 0xD8,0x41,0x14,0x55,0x0D,0x54,0x8B,0xB9,
+ 0xAD,0x46,0x0B,0xAF,0x80,0x52,0x2C,0xFA, 0x8C,0x89,0x66,0xFD,0xB2,0xA9,0x9B,0xC0,
+ };
+ MEMALIGN unsigned char r[GROUP_PARALLELISM*(8+56)]; /* 56 because we will move back in memory while looping */
+ MEMALIGN unsigned char sbox_in[GROUP_PARALLELISM],sbox_out[GROUP_PARALLELISM],perm_out[GROUP_PARALLELISM];
+ int roff;
+ int i,g,count_all=GROUP_PARALLELISM;
+
+ roff=GROUP_PARALLELISM*56;
+
+#define FASTTRASP1
+#ifndef FASTTRASP1
+ for(g=0;g<count;g++){
+ // Init registers
+ int j;
+ for(j=0;j<8;j++){
+ r[roff+GROUP_PARALLELISM*j+g]=ib[8*g+j];
+ }
+ }
+#else
+ trasp_N_8((unsigned char *)&r[roff],(unsigned char *)ib,count);
+#endif
+//dump_mem("OLD r[roff]",&r[roff],GROUP_PARALLELISM*8,GROUP_PARALLELISM);
+
+ // loop over kk[55]..kk[0]
+ for(i=55;i>=0;i--){
+ {
+ MEMALIGN batch tkkmulti=kkmulti[i];
+ batch *si=(batch *)sbox_in;
+ batch *r6_N=(batch *)(r+roff+GROUP_PARALLELISM*6);
+ for(g=0;g<count_all/BYTES_PER_BATCH;g++){
+ si[g]=B_FFXOR(tkkmulti,r6_N[g]); //FIXME: introduce FASTBATCH?
+ }
+ }
+
+ // table lookup, this works on only one byte at a time
+ // most difficult part of all
+ // - can't be parallelized
+ // - can't be synthetized through boolean terms (8 input bits are too many)
+ for(g=0;g<count_all;g++){
+ sbox_out[g]=block_sbox[sbox_in[g]];
+ }
+
+ // bit permutation
+ {
+ unsigned char *po=(unsigned char *)perm_out;
+ unsigned char *so=(unsigned char *)sbox_out;
+//dump_mem("pre perm ",(unsigned char *)so,GROUP_PARALLELISM,GROUP_PARALLELISM);
+ for(g=0;g<count_all;g+=BYTES_PER_BATCH){
+ MEMALIGN batch in,out;
+ in=*(batch *)&so[g];
+
+ out=B_FFOR(
+ B_FFOR(
+ B_FFOR(
+ B_FFOR(
+ B_FFOR(
+ B_FFSH8L(B_FFAND(in,B_FFN_ALL_29()),1),
+ B_FFSH8L(B_FFAND(in,B_FFN_ALL_02()),6)),
+ B_FFSH8L(B_FFAND(in,B_FFN_ALL_04()),3)),
+ B_FFSH8R(B_FFAND(in,B_FFN_ALL_10()),2)),
+ B_FFSH8R(B_FFAND(in,B_FFN_ALL_40()),6)),
+ B_FFSH8R(B_FFAND(in,B_FFN_ALL_80()),4));
+
+ *(batch *)&po[g]=out;
+ }
+//dump_mem("post perm",(unsigned char *)po,GROUP_PARALLELISM,GROUP_PARALLELISM);
+ }
+
+ roff-=GROUP_PARALLELISM; /* virtual shift of registers */
+
+#if 0
+/* one by one */
+ for(g=0;g<count_all;g++){
+ r[roff+GROUP_PARALLELISM*0+g]=r[roff+GROUP_PARALLELISM*8+g]^sbox_out[g];
+ r[roff+GROUP_PARALLELISM*6+g]^=perm_out[g];
+ r[roff+GROUP_PARALLELISM*4+g]^=r[roff+GROUP_PARALLELISM*0+g];
+ r[roff+GROUP_PARALLELISM*3+g]^=r[roff+GROUP_PARALLELISM*0+g];
+ r[roff+GROUP_PARALLELISM*2+g]^=r[roff+GROUP_PARALLELISM*0+g];
+ }
+#else
+ for(g=0;g<count_all;g+=BEST_SPAN){
+ XOR_BEST_BY(&r[roff+GROUP_PARALLELISM*0+g],&r[roff+GROUP_PARALLELISM*8+g],&sbox_out[g]);
+ XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*6+g],&perm_out[g]);
+ XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*4+g],&r[roff+GROUP_PARALLELISM*0+g]);
+ XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*3+g],&r[roff+GROUP_PARALLELISM*0+g]);
+ XOREQ_BEST_BY(&r[roff+GROUP_PARALLELISM*2+g],&r[roff+GROUP_PARALLELISM*0+g]);
+ }
+#endif
+ }
+
+#define FASTTRASP2
+#ifndef FASTTRASP2
+ for(g=0;g<count;g++){
+ // Copy results
+ int j;
+ for(j=0;j<8;j++){
+ bd[8*g+j]=r[roff+GROUP_PARALLELISM*j+g];
+ }
+ }
+#else
+ trasp_8_N((unsigned char *)&r[roff],(unsigned char *)bd,count);
+#endif
+}
+
+//-----------------------------------EXTERNAL INTERFACE
+
+//-----get internal parallelism
+
+int get_internal_parallelism(void){
+ return GROUP_PARALLELISM;
+}
+
+//-----get suggested cluster size
+
+int get_suggested_cluster_size(void){
+ int r;
+ r=GROUP_PARALLELISM+GROUP_PARALLELISM/10;
+ if(r<GROUP_PARALLELISM+5) r=GROUP_PARALLELISM+5;
+ return r;
+}
+
+//-----key structure
+
+void *get_key_struct(void){
+ struct csa_keys_t *keys=(struct csa_keys_t *)MALLOC(sizeof(struct csa_keys_t));
+ if(keys) {
+ static const unsigned char pk[8] = { 0,0,0,0,0,0,0,0 };
+ set_control_words(keys,pk,pk);
+ }
+ return keys;
+}
+
+void free_key_struct(void *keys){
+ return FREE(keys);
+}
+
+//-----set control words
+
+static void schedule_key(struct csa_key_t *key, const unsigned char *pk){
+ // could be made faster, but is not run often
+ int bi,by;
+ int i,j;
+// key
+ memcpy(key->ck,pk,8);
+// precalculations for stream
+ key_schedule_stream(key->ck,key->iA,key->iB);
+ for(by=0;by<8;by++){
+ for(bi=0;bi<8;bi++){
+ key->ck_g[by][bi]=(key->ck[by]&(1<<bi))?FF1():FF0();
+ }
+ }
+ for(by=0;by<8;by++){
+ for(bi=0;bi<4;bi++){
+ key->iA_g[by][bi]=(key->iA[by]&(1<<bi))?FF1():FF0();
+ key->iB_g[by][bi]=(key->iB[by]&(1<<bi))?FF1():FF0();
+ }
+ }
+// precalculations for block
+ key_schedule_block(key->ck,key->kk);
+ for(i=0;i<56;i++){
+ for(j=0;j<BYTES_PER_BATCH;j++){
+ *(((unsigned char *)&key->kkmulti[i])+j)=key->kk[i];
+ }
+ }
+}
+
+void set_control_words(void *keys, const unsigned char *ev, const unsigned char *od){
+ schedule_key(&((struct csa_keys_t *)keys)->even,ev);
+ schedule_key(&((struct csa_keys_t *)keys)->odd,od);
+}
+
+void set_even_control_word(void *keys, const unsigned char *pk){
+ schedule_key(&((struct csa_keys_t *)keys)->even,pk);
+}
+
+void set_odd_control_word(void *keys, const unsigned char *pk){
+ schedule_key(&((struct csa_keys_t *)keys)->odd,pk);
+}
+
+//-----get control words
+
+void get_control_words(void *keys, unsigned char *even, unsigned char *odd){
+ memcpy(even,&((struct csa_keys_t *)keys)->even.ck,8);
+ memcpy(odd,&((struct csa_keys_t *)keys)->odd.ck,8);
+}
+
+//----- decrypt
+
+int decrypt_packets(void *keys, unsigned char **cluster){
+ // statistics, currently unused
+ int stat_no_scramble=0;
+ int stat_reserved=0;
+ int stat_decrypted[2]={0,0};
+ int stat_decrypted_mini=0;
+ unsigned char **clst;
+ unsigned char **clst2;
+ int grouped;
+ int group_ev_od;
+ int advanced;
+ int can_advance;
+ unsigned char *g_pkt[GROUP_PARALLELISM];
+ int g_len[GROUP_PARALLELISM];
+ int g_offset[GROUP_PARALLELISM];
+ int g_n[GROUP_PARALLELISM];
+ int g_residue[GROUP_PARALLELISM];
+ unsigned char *pkt;
+ int xc0,ev_od,len,offset,n,residue;
+ struct csa_key_t* k;
+ int i,j,iter,g;
+ int t23,tsmall;
+ int alive[24];
+//icc craziness int pad1=0; //////////align! FIXME
+ unsigned char *encp[GROUP_PARALLELISM];
+ unsigned char stream_in[GROUP_PARALLELISM*8];
+ unsigned char stream_out[GROUP_PARALLELISM*8];
+ MEMALIGN unsigned char ib[GROUP_PARALLELISM*8];
+ MEMALIGN unsigned char block_out[GROUP_PARALLELISM*8];
+ struct stream_regs regs;
+
+//icc craziness i=(int)&pad1;//////////align!!! FIXME
+
+ // build a list of packets to be processed
+ clst=cluster;
+ grouped=0;
+ advanced=0;
+ can_advance=1;
+ group_ev_od=-1; // silence incorrect compiler warning
+ pkt=*clst;
+ do{ // find a new packet
+ if(grouped==GROUP_PARALLELISM){
+ // full
+ break;
+ }
+ if(pkt==NULL){
+ // no more ranges
+ break;
+ }
+ if(pkt>=*(clst+1)){
+ // out of this range, try next
+ clst++;clst++;
+ pkt=*clst;
+ continue;
+ }
+
+ do{ // handle this packet
+ xc0=pkt[3]&0xc0;
+ DBG(fprintf(stderr," exam pkt=%p, xc0=%02x, can_adv=%i\n",pkt,xc0,can_advance));
+ if(xc0==0x00){
+ DBG(fprintf(stderr,"skip clear pkt %p (can_advance is %i)\n",pkt,can_advance));
+ advanced+=can_advance;
+ stat_no_scramble++;
+ break;
+ }
+ if(xc0==0x40){
+ DBG(fprintf(stderr,"skip reserved pkt %p (can_advance is %i)\n",pkt,can_advance));
+ advanced+=can_advance;
+ stat_reserved++;
+ break;
+ }
+ if(xc0==0x80||xc0==0xc0){ // encrypted
+ ev_od=(xc0&0x40)>>6; // 0 even, 1 odd
+ if(grouped==0) group_ev_od=ev_od; // this group will be all even (or odd)
+ if(group_ev_od==ev_od){ // could be added to group
+ pkt[3]&=0x3f; // consider it decrypted now
+ if(pkt[3]&0x20){ // incomplete packet
+ offset=4+pkt[4]+1;
+ len=188-offset;
+ n=len>>3;
+ residue=len-(n<<3);
+ if(n==0){ // decrypted==encrypted!
+ DBG(fprintf(stderr,"DECRYPTED MINI! (can_advance is %i)\n",can_advance));
+ advanced+=can_advance;
+ stat_decrypted_mini++;
+ break; // this doesn't need more processing
+ }
+ }else{
+ len=184;
+ offset=4;
+ n=23;
+ residue=0;
+ }
+ g_pkt[grouped]=pkt;
+ g_len[grouped]=len;
+ g_offset[grouped]=offset;
+ g_n[grouped]=n;
+ g_residue[grouped]=residue;
+ DBG(fprintf(stderr,"%2i: eo=%i pkt=%p len=%03i n=%2i residue=%i\n",grouped,ev_od,pkt,len,n,residue));
+ grouped++;
+ advanced+=can_advance;
+ stat_decrypted[ev_od]++;
+ }
+ else{
+ can_advance=0;
+ DBG(fprintf(stderr,"skip pkt %p and can_advance set to 0\n",pkt));
+ break; // skip and go on
+ }
+ }
+ } while(0);
+
+ if(can_advance){
+ // move range start forward
+ *clst+=188;
+ }
+ // next packet, if there is one
+ pkt+=188;
+ } while(1);
+ DBG(fprintf(stderr,"-- result: grouped %i pkts, advanced %i pkts\n",grouped,advanced));
+
+ // delete empty ranges and compact list
+ clst2=cluster;
+ for(clst=cluster;*clst!=NULL;clst+=2){
+ // if not empty
+ if(*clst<*(clst+1)){
+ // it will remain
+ *clst2=*clst;
+ *(clst2+1)=*(clst+1);
+ clst2+=2;
+ }
+ }
+ *clst2=NULL;
+
+ if(grouped==0){
+ // no processing needed
+ return advanced;
+ }
+
+ // sort them, longest payload first
+ // we expect many n=23 packets and a few n<23
+ DBG(fprintf(stderr,"PRESORTING\n"));
+ for(i=0;i<grouped;i++){
+ DBG(fprintf(stderr,"%2i of %2i: pkt=%p len=%03i n=%2i residue=%i\n",i,grouped,g_pkt[i],g_len[i],g_n[i],g_residue[i]));
+ }
+ // grouped is always <= GROUP_PARALLELISM
+
+#define g_swap(a,b) \
+ pkt=g_pkt[a]; \
+ g_pkt[a]=g_pkt[b]; \
+ g_pkt[b]=pkt; \
+\
+ len=g_len[a]; \
+ g_len[a]=g_len[b]; \
+ g_len[b]=len; \
+\
+ offset=g_offset[a]; \
+ g_offset[a]=g_offset[b]; \
+ g_offset[b]=offset; \
+\
+ n=g_n[a]; \
+ g_n[a]=g_n[b]; \
+ g_n[b]=n; \
+\
+ residue=g_residue[a]; \
+ g_residue[a]=g_residue[b]; \
+ g_residue[b]=residue;
+
+ // step 1: move n=23 packets before small packets
+ t23=0;
+ tsmall=grouped-1;
+ for(;;){
+ for(;t23<grouped;t23++){
+ if(g_n[t23]!=23) break;
+ }
+DBG(fprintf(stderr,"t23 after for =%i\n",t23));
+
+ for(;tsmall>=0;tsmall--){
+ if(g_n[tsmall]==23) break;
+ }
+DBG(fprintf(stderr,"tsmall after for =%i\n",tsmall));
+
+ if(tsmall-t23<1) break;
+
+DBG(fprintf(stderr,"swap t23=%i,tsmall=%i\n",t23,tsmall));
+
+ g_swap(t23,tsmall);
+
+ t23++;
+ tsmall--;
+DBG(fprintf(stderr,"new t23=%i,tsmall=%i\n\n",t23,tsmall));
+ }
+ DBG(fprintf(stderr,"packets with n=23, t23=%i grouped=%i\n",t23,grouped));
+ DBG(fprintf(stderr,"MIDSORTING\n"));
+ for(i=0;i<grouped;i++){
+ DBG(fprintf(stderr,"%2i of %2i: pkt=%p len=%03i n=%2i residue=%i\n",i,grouped,g_pkt[i],g_len[i],g_n[i],g_residue[i]));
+ }
+
+ // step 2: sort small packets in decreasing order of n (bubble sort is enough)
+ for(i=t23;i<grouped;i++){
+ for(j=i+1;j<grouped;j++){
+ if(g_n[j]>g_n[i]){
+ g_swap(i,j);
+ }
+ }
+ }
+ DBG(fprintf(stderr,"POSTSORTING\n"));
+ for(i=0;i<grouped;i++){
+ DBG(fprintf(stderr,"%2i of %2i: pkt=%p len=%03i n=%2i residue=%i\n",i,grouped,g_pkt[i],g_len[i],g_n[i],g_residue[i]));
+ }
+
+ // we need to know how many packets need 23 iterations, how many 22...
+ for(i=0;i<=23;i++){
+ alive[i]=0;
+ }
+ // count
+ alive[23-1]=t23;
+ for(i=t23;i<grouped;i++){
+ alive[g_n[i]-1]++;
+ }
+ // integrate
+ for(i=22;i>=0;i--){
+ alive[i]+=alive[i+1];
+ }
+ DBG(fprintf(stderr,"ALIVE\n"));
+ for(i=0;i<=23;i++){
+ DBG(fprintf(stderr,"alive%2i=%i\n",i,alive[i]));
+ }
+
+ // choose key
+ if(group_ev_od==0){
+ k=&((struct csa_keys_t *)keys)->even;
+ }
+ else{
+ k=&((struct csa_keys_t *)keys)->odd;
+ }
+
+ //INIT
+//#define INITIALIZE_UNUSED_INPUT
+#ifdef INITIALIZE_UNUSED_INPUT
+// unnecessary zeroing.
+// without this, we operate on uninitialized memory
+// when grouped<GROUP_PARALLELISM, but it's not a problem,
+// as final results will be discarded.
+// random data makes debugging sessions difficult.
+ for(j=0;j<GROUP_PARALLELISM*8;j++) stream_in[j]=0;
+DBG(fprintf(stderr,"--- WARNING: you could gain speed by not initializing unused memory ---\n"));
+#else
+DBG(fprintf(stderr,"--- WARNING: DEBUGGING IS MORE DIFFICULT WHEN PROCESSING RANDOM DATA CHANGING AT EVERY RUN! ---\n"));
+#endif
+
+ for(g=0;g<grouped;g++){
+ encp[g]=g_pkt[g];
+ DBG(fprintf(stderr,"header[%i]=%p (%02x)\n",g,encp[g],*(encp[g])));
+ encp[g]+=g_offset[g]; // skip header
+ FFTABLEIN(stream_in,g,encp[g]);
+ }
+//dump_mem("stream_in",stream_in,GROUP_PARALLELISM*8,BYPG);
+
+
+ // ITER 0
+DBG(fprintf(stderr,">>>>>ITER 0\n"));
+ iter=0;
+ stream_cypher_group_init(®s,k->iA_g,k->iB_g,stream_in);
+ // fill first ib
+ for(g=0;g<alive[iter];g++){
+ COPY_8_BY(ib+8*g,encp[g]);
+ }
+DBG(dump_mem("IB ",ib,8*alive[iter],8));
+ // ITER 1..N-1
+ for (iter=1;iter<23&&alive[iter-1]>0;iter++){
+DBG(fprintf(stderr,">>>>>ITER %i\n",iter));
+ // alive and just dead packets: calc block
+ block_decypher_group(k->kkmulti,ib,block_out,alive[iter-1]);
+DBG(dump_mem("BLO_ib ",block_out,8*alive[iter-1],8));
+ // all packets (dead too): calc stream
+ stream_cypher_group_normal(®s,stream_out);
+//dump_mem("stream_out",stream_out,GROUP_PARALLELISM*8,BYPG);
+
+ // alive packets: calc ib
+ for(g=0;g<alive[iter];g++){
+ FFTABLEOUT(ib+8*g,stream_out,g);
+DBG(dump_mem("stream_out_ib ",ib+8*g,8,8));
+// XOREQ8BY gcc bug? 2x4 ok, 8 ko UPDATE: result ok but speed 1-2% slower (!!!???)
+#if 1
+ XOREQ_4_BY(ib+8*g,encp[g]+8);
+ XOREQ_4_BY(ib+8*g+4,encp[g]+8+4);
+#else
+ XOREQ_8_BY(ib+8*g,encp[g]+8);
+#endif
+DBG(dump_mem("after_stream_xor_ib ",ib+8*g,8,8));
+ }
+ // alive packets: decrypt data
+ for(g=0;g<alive[iter];g++){
+DBG(dump_mem("before_ib_decrypt_data ",encp[g],8,8));
+ XOR_8_BY(encp[g],ib+8*g,block_out+8*g);
+DBG(dump_mem("after_ib_decrypt_data ",encp[g],8,8));
+ }
+ // just dead packets: write decrypted data
+ for(g=alive[iter];g<alive[iter-1];g++){
+DBG(dump_mem("jd_before_ib_decrypt_data ",encp[g],8,8));
+ COPY_8_BY(encp[g],block_out+8*g);
+DBG(dump_mem("jd_after_ib_decrypt_data ",encp[g],8,8));
+ }
+ // just dead packets: decrypt residue
+ for(g=alive[iter];g<alive[iter-1];g++){
+DBG(dump_mem("jd_before_decrypt_residue ",encp[g]+8,g_residue[g],g_residue[g]));
+ FFTABLEOUTXORNBY(g_residue[g],encp[g]+8,stream_out,g);
+DBG(dump_mem("jd_after_decrypt_residue ",encp[g]+8,g_residue[g],g_residue[g]));
+ }
+ // alive packets: pointers++
+ for(g=0;g<alive[iter];g++) encp[g]+=8;
+ };
+ // ITER N
+DBG(fprintf(stderr,">>>>>ITER 23\n"));
+ iter=23;
+ // calc block
+ block_decypher_group(k->kkmulti,ib,block_out,alive[iter-1]);
+DBG(dump_mem("23BLO_ib ",block_out,8*alive[iter-1],8));
+ // just dead packets: write decrypted data
+ for(g=alive[iter];g<alive[iter-1];g++){
+DBG(dump_mem("23jd_before_ib_decrypt_data ",encp[g],8,8));
+ COPY_8_BY(encp[g],block_out+8*g);
+DBG(dump_mem("23jd_after_ib_decrypt_data ",encp[g],8,8));
+ }
+ // no residue possible
+ // so do nothing
+
+ DBG(fprintf(stderr,"returning advanced=%i\n",advanced));
+
+ M_EMPTY(); // restore CPU multimedia state
+
+ return advanced;
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#ifndef FFDECSA_H
+#define FFDECSA_H
+
+//----- public interface
+
+// -- how many packets can be decrypted at the same time
+// This is an info about internal decryption parallelism.
+// You should try to call decrypt_packets with more packets than the number
+// returned here for performance reasons (use get_suggested_cluster_size to know
+// how many).
+int get_internal_parallelism(void);
+
+// -- how many packets you should have in a cluster when calling decrypt_packets
+// This is a suggestion to achieve optimal performance; typically a little
+// higher than what get_internal_parallelism returns.
+// Passing less packets could slow down the decryption.
+// Passing more packets is never bad (if you don't spend a lot of time building
+// the list).
+int get_suggested_cluster_size(void);
+
+// -- alloc & free the key structure
+void *get_key_struct(void);
+void free_key_struct(void *keys);
+
+// -- set control words, 8 bytes each
+void set_control_words(void *keys, const unsigned char *even, const unsigned char *odd);
+
+// -- set even control word, 8 bytes
+void set_even_control_word(void *keys, const unsigned char *even);
+
+// -- set odd control word, 8 bytes
+void set_odd_control_word(void *keys, const unsigned char *odd);
+
+// -- get control words, 8 bytes each
+//void get_control_words(void *keys, unsigned char *even, unsigned char *odd);
+
+// -- decrypt many TS packets
+// This interface is a bit complicated because it is designed for maximum speed.
+// Please read doc/how_to_use.txt.
+int decrypt_packets(void *keys, unsigned char **cluster);
+
+#endif
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <string.h>
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "FFdecsa.h"
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#include "FFdecsa_test_testcases.h"
+
+int compare(unsigned char *p1, unsigned char *p2, int n, int silently){
+ int i;
+ int ok=1;
+ for(i=0;i<n;i++){
+ if(i==3) continue; // tolerate this
+ if(p1[i]!=p2[i]){
+ fprintf(stderr,"at pos 0x%02x, got 0x%02x instead of 0x%02x\n",i,p1[i],p2[i]);
+ ok=0;
+ }
+ }
+ if(!silently){
+ if(ok){
+ fprintf(stderr,"CORRECT!\n");
+ }
+ else{
+ fprintf(stderr,"FAILED!\n");
+ }
+ }
+ return ok;
+}
+
+
+//MAIN
+
+#define TS_PKTS_FOR_TEST 30*1000
+//#define TS_PKTS_FOR_TEST 1000*1000
+unsigned char megabuf[188*TS_PKTS_FOR_TEST];
+unsigned char onebuf[188];
+
+unsigned char *cluster[10];
+
+int main(void){
+ int i;
+ struct timeval tvs,tve;
+ void *keys=get_key_struct();
+
+ fprintf(stderr,"FFdecsa 1.0: testing correctness and speed\n");
+
+/* begin correctness testing */
+
+ set_control_words(keys,test_invalid_key,test_1_key);
+ memcpy(onebuf,test_1_encrypted,188);
+ cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+ decrypt_packets(keys,cluster);
+ compare(onebuf,test_1_expected,188,0);
+
+ set_control_words(keys,test_2_key,test_invalid_key);
+ memcpy(onebuf,test_2_encrypted,188);
+ cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+ decrypt_packets(keys,cluster);
+ compare(onebuf,test_2_expected,188,0);
+
+ set_control_words(keys,test_3_key,test_invalid_key);
+ memcpy(onebuf,test_3_encrypted,188);
+ cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+ decrypt_packets(keys,cluster);
+ compare(onebuf,test_3_expected,188,0);
+
+ set_control_words(keys,test_p_10_0_key,test_invalid_key);
+ memcpy(onebuf,test_p_10_0_encrypted,188);
+ cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+ decrypt_packets(keys,cluster);
+ compare(onebuf,test_p_10_0_expected,188,0);
+
+ set_control_words(keys,test_p_1_6_key,test_invalid_key);
+ memcpy(onebuf,test_p_1_6_encrypted,188);
+ cluster[0]=onebuf;cluster[1]=onebuf+188;cluster[2]=NULL;
+ decrypt_packets(keys,cluster);
+ compare(onebuf,test_p_1_6_expected,188,0);
+
+/* begin speed testing */
+
+#if 0
+// test on short packets
+#define s_encrypted test_p_1_6_encrypted
+#define s_key_e test_p_1_6_key
+#define s_key_o test_invalid_key
+#define s_expected test_p_1_6_expected
+
+#else
+//test on full packets
+#define s_encrypted test_2_encrypted
+#define s_key_e test_2_key
+#define s_key_o test_invalid_key
+#define s_expected test_2_expected
+
+#endif
+
+ for(i=0;i<TS_PKTS_FOR_TEST;i++){
+ memcpy(&megabuf[188*i],s_encrypted,188);
+ }
+// test that packets are not shuffled around
+// so, let's put an undecryptable packet somewhere in the middle (we will use a wrong key)
+#define noONE_POISONED_PACKET
+#ifdef ONE_POISONED_PACKET
+ memcpy(&megabuf[188*(TS_PKTS_FOR_TEST*2/3)],test_3_encrypted,188);
+#endif
+
+ // start decryption
+ set_control_words(keys,s_key_e,s_key_o);
+ gettimeofday(&tvs,NULL);
+#if 0
+// force one by one
+ for(i=0;i<TS_PKTS_FOR_TEST;i++){
+ cluster[0]=megabuf+188*i;cluster[1]=onebuf+188*i+188;cluster[2]=NULL;
+ decrypt_packets(keys,cluster);
+ }
+#else
+ {
+ int done=0;
+ while(done<TS_PKTS_FOR_TEST){
+ //fprintf(stderr,"done=%i\n",done);
+ cluster[0]=megabuf+188*done;cluster[1]=megabuf+188*TS_PKTS_FOR_TEST;cluster[2]=NULL;
+ done+=decrypt_packets(keys,cluster);
+ }
+ }
+#endif
+ gettimeofday(&tve,NULL);
+ //end decryption
+
+ fprintf(stderr,"speed=%f Mbit/s\n",(184*TS_PKTS_FOR_TEST*8)/((tve.tv_sec-tvs.tv_sec)+1e-6*(tve.tv_usec-tvs.tv_usec))/1000000);
+ fprintf(stderr,"speed=%f pkts/s\n",TS_PKTS_FOR_TEST/((tve.tv_sec-tvs.tv_sec)+1e-6*(tve.tv_usec-tvs.tv_usec)));
+
+ // this packet couldn't be decrypted correctly
+#ifdef ONE_POISONED_PACKET
+ compare(megabuf+188*(TS_PKTS_FOR_TEST*2/3),test_3_expected,188,0); /* will fail because we used a wrong key */
+#endif
+ // these should be ok
+ compare(megabuf,s_expected,188,0);
+ compare(megabuf+188*511,s_expected,188,0);
+ compare(megabuf+188*512,s_expected,188,0);
+ compare(megabuf+188*319,s_expected,188,0);
+ compare(megabuf+188*(TS_PKTS_FOR_TEST-1),s_expected,188,0);
+
+ for(i=0;i<TS_PKTS_FOR_TEST;i++){
+ if(!compare(megabuf+188*i,s_expected,188,1)){
+ fprintf(stderr,"FAILED COMPARISON OF PACKET %10i\n",i);
+ };
+ }
+
+ return 0;
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+// TEST DATA
+
+////////// used as a wrong key
+unsigned char test_invalid_key[0x08] = {
+ 0x0f, 0x1e, 0x2d, 0x3c, 0x4b, 0x5a, 0x69, 0x78
+};
+
+
+////////// test 1: odd key
+unsigned char test_1_key[0x8] = {
+ 0x07, 0xe0, 0x1b, 0x02, 0xc9, 0xe0, 0x45, 0xee
+};
+unsigned char test_1_encrypted[0x100] = {
+ 0x47, 0x00, 0x00, 0xd0,
+ 0xde, 0xcf, 0x0a, 0x0d, 0xb2, 0xd7, 0xc4, 0x40, 0xde, 0x5d, 0x63, 0x18, 0x5a, 0x98, 0x17, 0xaa,
+ 0xc9, 0xbc, 0x27, 0xc6, 0xcb, 0x49, 0x40, 0x48, 0xfd, 0x20, 0xb7, 0x05, 0x5b, 0x27, 0xcb, 0xeb,
+ 0x9a, 0xf0, 0xac, 0x45, 0x6d, 0x56, 0xf4, 0x7b, 0x6f, 0xa0, 0x57, 0xf3, 0x9b, 0xf7, 0xa2, 0xc7,
+ 0xd4, 0x68, 0x24, 0x00, 0x2f, 0x28, 0x13, 0x96, 0x94, 0xa8, 0x7c, 0xf4, 0x6f, 0x07, 0x2a, 0x0e,
+ 0xe8, 0xa1, 0xeb, 0xc7, 0x80, 0xac, 0x1f, 0x79, 0xbf, 0x5d, 0xb6, 0x10, 0x7c, 0x2e, 0x52, 0xe9,
+ 0x34, 0x2c, 0xa8, 0x39, 0x01, 0x73, 0x04, 0x24, 0xa8, 0x1e, 0xdb, 0x5b, 0xcb, 0x24, 0xf6, 0x31,
+ 0xab, 0x02, 0x6b, 0xf9, 0xf6, 0xf7, 0xe9, 0x52, 0xad, 0xcf, 0x62, 0x0f, 0x42, 0xf6, 0x66, 0x5d,
+ 0xc0, 0x86, 0xf2, 0x7b, 0x40, 0x20, 0xa9, 0xbd, 0x1f, 0xfd, 0x16, 0xad, 0x2e, 0x75, 0xa6, 0xa0,
+ 0x85, 0xf3, 0x9c, 0x31, 0x20, 0x4e, 0xfb, 0x95, 0x61, 0x78, 0xce, 0x10, 0xc1, 0x48, 0x5f, 0xd3,
+ 0x61, 0x05, 0x12, 0xf4, 0xe2, 0x04, 0xae, 0xe0, 0x86, 0x01, 0x56, 0x55, 0xb1, 0x0f, 0xa6, 0x33,
+ 0x95, 0x20, 0x92, 0xf0, 0xbe, 0x39, 0x31, 0xe1, 0x2a, 0xf7, 0x93, 0xb4, 0xf7, 0xe4, 0xf1, 0x85,
+ 0xae, 0x50, 0xf1, 0x63, 0xd4, 0x5d, 0x9c, 0x6c
+};
+unsigned char test_1_expected[0x100] = {
+ 0x47, 0x00, 0x00, 0xd0,
+ 0xaf, 0xbe, 0xfb, 0xef, 0xbe, 0xfb, 0xef, 0xbe, 0xfb, 0xef, 0xbe, 0xfb, 0xe6, 0xb5, 0xad, 0x7c,
+ 0xf9, 0xf3, 0xe5, 0xb1, 0x6c, 0x7c, 0xf9, 0xf3, 0xe6, 0xb5, 0xad, 0x6b, 0x5f, 0x3e, 0x7c, 0xf9,
+ 0x6c, 0x5b, 0x1f, 0x3e, 0x7c, 0xf9, 0xad, 0x6b, 0x5a, 0xd7, 0xcf, 0x9f, 0x3e, 0x5b, 0x16, 0xc7,
+ 0xcf, 0x9f, 0x3e, 0x6b, 0x5a, 0xd6, 0xb5, 0xf3, 0xe7, 0xcf, 0x96, 0xc5, 0xb1, 0xf3, 0xe7, 0xcf,
+ 0x9a, 0xd6, 0xb5, 0xad, 0x7c, 0xf9, 0xf3, 0xe5, 0xb1, 0x6c, 0x7c, 0xf9, 0xf3, 0xe6, 0xb5, 0xad,
+ 0x6b, 0x5f, 0x3e, 0x7c, 0xf9, 0x6c, 0x5b, 0x1f, 0x3e, 0x7c, 0xf9, 0xad, 0x6b, 0x5a, 0xd7, 0xcf,
+ 0x9f, 0x3e, 0x5b, 0x16, 0xc7, 0xcf, 0x9f, 0x3e, 0x6b, 0x5a, 0xd6, 0xb5, 0xf3, 0xe7, 0xcf, 0x96,
+ 0xc5, 0xb1, 0xf3, 0xe7, 0xcf, 0x9a, 0xd6, 0xb5, 0xad, 0x7c, 0xf9, 0xf3, 0xe5, 0xb1, 0x6c, 0x7c,
+ 0xf9, 0xf3, 0xe6, 0xb5, 0xad, 0x6b, 0x5f, 0x3e, 0x7c, 0xf9, 0x6c, 0x5b, 0x1f, 0x3e, 0x7c, 0xf9,
+ 0xad, 0x6b, 0x5a, 0xd7, 0xcf, 0x9f, 0x3e, 0x5b, 0x16, 0xc7, 0xcf, 0x9f, 0x3e, 0x6b, 0x5a, 0xd6,
+ 0xb5, 0xf3, 0xe7, 0xcf, 0x96, 0xc5, 0xb1, 0xf3, 0xe7, 0xcf, 0x9a, 0xd0, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xfc, 0x44, 0x00, 0x66, 0xb1, 0x11, 0x11
+};
+unsigned char test_1_expected_stream[0x100] = {
+ 0xdc, 0x15, 0xde, 0xf1, 0x4a, 0xf1, 0xf8, 0x2c,
+ 0x75, 0xc8, 0x3a, 0x1f, 0xbf, 0x67, 0x19, 0xe1,
+ 0xf4, 0x6c, 0x78, 0x99, 0x48, 0xaf, 0xef, 0x94,
+ 0x71, 0x6b, 0x23, 0x9e, 0x29, 0x69, 0x2d, 0xa1,
+ 0x8a, 0xbb, 0xf4, 0x16, 0x68, 0xa5, 0x7f, 0x14,
+ 0xa9, 0x37, 0x24, 0x05, 0x5e, 0xdd, 0xec, 0x4b,
+ 0xb5, 0xcb, 0x7f, 0x1d, 0xa7, 0x09, 0x2a, 0xce,
+ 0xc4, 0x30, 0x83, 0xfd, 0xd9, 0x88, 0xa9, 0xf3,
+ 0x85, 0x9c, 0x38, 0x31, 0x88, 0xac, 0x74, 0x02,
+ 0x44, 0xdc, 0xb7, 0x81, 0x07, 0xc8, 0x1b, 0x03,
+ 0x9c, 0x76, 0xbe, 0xe9, 0x4d, 0x3e, 0x19, 0xad,
+ 0xe1, 0xf1, 0xa5, 0x13, 0xe8, 0xc0, 0x12, 0x57,
+ 0x68, 0xb1, 0x9c, 0x6c, 0x9f, 0x58, 0x78, 0xee,
+ 0x4f, 0x5b, 0x33, 0x1e, 0xc6, 0x29, 0xfc, 0x40,
+ 0x58, 0x22, 0xa2, 0xd8, 0x32, 0xdd, 0x29, 0x4f,
+ 0x2b, 0xe1, 0xef, 0xe4, 0xbb, 0xf2, 0x60, 0x94,
+ 0x6c, 0xc5, 0x51, 0xec, 0x35, 0x4c, 0x27, 0xc6,
+ 0x9d, 0x73, 0xe0, 0xf4, 0x2b, 0xfa, 0x62, 0x12,
+ 0xcd, 0x44, 0xbe, 0x57, 0xfe, 0x80, 0xe7, 0xa9,
+ 0x3c, 0x49, 0x42, 0xb6, 0xed, 0x05, 0x57, 0x00,
+ 0xd2, 0x25, 0x90, 0xb3, 0xe4, 0x65, 0x8f, 0xd6,
+ 0x4e, 0x0c, 0x73, 0x30, 0x3b, 0x68, 0x48, 0xdd,
+// stream ^ sb
+// 0x02, 0x48, 0xbd, 0xe9, 0x10, 0x69, 0xef, 0x86,
+// 0xbc, 0x74, 0x1d, 0xd9, 0x74, 0x2e, 0x59, 0xa9,
+// 0x09, 0x4c, 0xcf, 0x9c, 0x13, 0x88, 0x24, 0x7f,
+// 0xeb, 0x9b, 0x8f, 0xdb, 0x44, 0x3f, 0xd9, 0xda,
+};
+unsigned char test_1_expected_block[0x100] = {
+ 0xad, 0xf6, 0x46, 0x06, 0xae, 0x92, 0x00, 0x38,
+ 0x47, 0x9b, 0xa3, 0x22, 0x92, 0x9b, 0xf4, 0xd5,
+ 0xf0, 0xbf, 0x2a, 0x2d, 0x7f, 0xf4, 0xdd, 0x8c,
+ 0x0d, 0x2e, 0x22, 0xb0, 0x1b, 0x01, 0xa5, 0x23,
+ 0x89, 0x40, 0xbc, 0xdb, 0x8f, 0xab, 0x70, 0xb8,
+ 0x27, 0x88, 0xcf, 0x9a, 0x4f, 0xae, 0xe9, 0x1a,
+ 0xee, 0xfc, 0x3d, 0x82, 0x92, 0xd8, 0xb5, 0x33,
+ 0xcb, 0x5e, 0xfe, 0xff, 0xe8, 0xd7, 0x51, 0x45,
+ 0xa0, 0x17, 0x3b, 0x8c, 0x88, 0x7b, 0xd5, 0x0e,
+ 0xc1, 0x9c, 0x63, 0x41, 0xf5, 0x5d, 0xaa, 0x8a,
+ 0x5f, 0x37, 0x5b, 0xce, 0x7f, 0x76, 0xb4, 0x83,
+ 0x74, 0x8f, 0x37, 0x47, 0x75, 0x6d, 0x2c, 0xca,
+ 0x5a, 0x40, 0xa5, 0x75, 0x1a, 0x61, 0x81, 0x8d,
+ 0xe4, 0x87, 0x17, 0xd0, 0x75, 0xee, 0x9a, 0x6b,
+ 0x82, 0x6e, 0x47, 0x92, 0xd3, 0x32, 0x59, 0x5a,
+ 0x03, 0x6e, 0x8a, 0x26, 0x7e, 0x0d, 0xf7, 0x7d,
+ 0xf4, 0x4e, 0x79, 0x49, 0x59, 0x6f, 0x27, 0x2b,
+ 0x80, 0x8f, 0x9e, 0x5b, 0xd6, 0xc0, 0xb0, 0x0b,
+ 0xe6, 0x2e, 0xb2, 0xd5, 0x80, 0x10, 0x7f, 0xc1,
+ 0xbf, 0xae, 0x1f, 0xd9, 0x6d, 0x57, 0x3c, 0x37,
+ 0x4d, 0x21, 0xe4, 0xc8, 0x85, 0x44, 0xcf, 0xa0,
+ 0x07, 0x93, 0x18, 0x83, 0xef, 0x35, 0xd4, 0xb1,
+ 0xff, 0xfc, 0x44, 0x00, 0x66, 0xb1, 0x11, 0x11
+};
+unsigned char test_1_expected_kb[] = {
+ 0xEE, 0x45, 0xE0, 0xC9, 0x02, 0x1B, 0xE0, 0x07,
+ 0x46, 0xA4, 0x1C, 0x26, 0x7B, 0x0C, 0x01, 0xED,
+ 0x93, 0x99, 0xC3, 0x14, 0xC4, 0x4A, 0x8D, 0x54,
+ 0x19, 0x82, 0x39, 0xD1, 0x33, 0xB0, 0x33, 0x52,
+ 0x75, 0x62, 0x80, 0x3A, 0xC8, 0x83, 0x5E, 0x23,
+ 0xA2, 0x57, 0x0C, 0xC4, 0x2C, 0x2D, 0xD2, 0x98,
+ 0xA0, 0x6C, 0x77, 0x29, 0x11, 0x42, 0x49, 0xCE,
+};
+unsigned char test_1_expected_kk[] = {
+ 0x5e, 0x9d, 0xff, 0x2e, 0xbb, 0xaa, 0xa8, 0xe9,
+ 0xf6, 0x0e, 0xff, 0x7c, 0xda, 0xce, 0x55, 0x03,
+ 0xd9, 0xde, 0x79, 0xf5, 0x2c, 0xaf, 0x06, 0xf8,
+ 0xb2, 0xc9, 0xf8, 0x78, 0x54, 0xf9, 0xd1, 0xe7,
+ 0xeb, 0xbe, 0xd7, 0xeb, 0x25, 0xe9, 0x17, 0x99,
+ 0xbf, 0x24, 0xce, 0x2a, 0x73, 0xfe, 0xf9, 0xbc,
+ 0xd9, 0x55, 0x91, 0xcf, 0xe0, 0xc9, 0xdf, 0x88,
+};
+
+
+////////// test 2: even key
+unsigned char test_2_key[0x8] = {
+ 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
+};
+unsigned char test_2_encrypted[0x100] = {
+ 0x47, 0x00, 0x00, 0x90,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
+ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
+ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
+ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
+ 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
+ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+};
+unsigned char test_2_expected[0x100] = {
+ 0x47, 0x00, 0x00, 0x90,
+ 0x2d, 0x0a, 0x47, 0x20, 0x18, 0x11, 0x9c, 0x8a, 0xd1, 0x2a, 0x65, 0x6b, 0x89, 0xe4, 0x35, 0x2b,
+ 0xc2, 0xb5, 0x90, 0x61, 0xd1, 0x7e, 0x02, 0xe1, 0x3f, 0x46, 0x70, 0xcf, 0x77, 0x91, 0x2f, 0x22,
+ 0x93, 0xc1, 0x6c, 0xfe, 0x49, 0xad, 0x7c, 0xc2, 0xaf, 0x86, 0x1b, 0xa3, 0x29, 0xbe, 0xaa, 0x64,
+ 0xf0, 0x22, 0xb9, 0x5e, 0x98, 0xaa, 0x60, 0xef, 0xdf, 0xd6, 0x44, 0x77, 0xe6, 0xbf, 0xbb, 0x94,
+ 0xb2, 0x0a, 0x63, 0x0e, 0x5c, 0xf2, 0xac, 0xb4, 0x49, 0xcc, 0x9e, 0x4f, 0x94, 0x4c, 0x30, 0x12,
+ 0xe8, 0x55, 0xc2, 0x44, 0xa4, 0x52, 0xcb, 0x61, 0x81, 0xc9, 0xb6, 0xa6, 0x6b, 0xef, 0xaf, 0xa6,
+ 0x71, 0x1d, 0x7b, 0x58, 0x2f, 0xfa, 0xd1, 0x0c, 0x07, 0x9d, 0x1f, 0x35, 0x87, 0xbe, 0x02, 0x9f,
+ 0x20, 0xc6, 0x60, 0x8f, 0x1c, 0x30, 0x0f, 0x96, 0xd0, 0x71, 0xd6, 0x51, 0x10, 0xdf, 0x5b, 0xf6,
+ 0x44, 0x2f, 0x80, 0x28, 0xb7, 0xec, 0x23, 0x59, 0x4b, 0x94, 0x0b, 0x9a, 0x74, 0xa1, 0x1f, 0xf7,
+ 0x9e, 0x76, 0xb4, 0xdf, 0xbb, 0x3c, 0x8c, 0x88, 0x97, 0x22, 0x56, 0x73, 0x16, 0x05, 0xac, 0xf9,
+ 0x4f, 0x77, 0x9d, 0x38, 0xa0, 0x6b, 0x05, 0xd2, 0xe6, 0x15, 0x01, 0xb1, 0x5c, 0xc9, 0x62, 0xa9,
+ 0x9b, 0x1a, 0x6a, 0x1a, 0xcf, 0xe6, 0xa8, 0xba,
+};
+
+
+////////// test 3: even key
+unsigned char test_3_key[0x8] = {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+unsigned char test_3_encrypted[0x100] = {
+ 0x47, 0x00, 0x00, 0x90,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+
+};
+unsigned char test_3_expected[0x100] = {
+ 0x47, 0x00, 0x00, 0x90,
+ 0xfe, 0x91, 0xa7, 0x2f, 0xbf, 0xb0, 0x6a, 0x54, 0xc1, 0xe4, 0x33, 0x27, 0x18, 0xd5, 0x9c, 0x43,
+ 0xea, 0xaa, 0x6b, 0x38, 0x5c, 0xe7, 0xae, 0xc9, 0xac, 0xec, 0xef, 0xc3, 0x51, 0x7d, 0x53, 0x47,
+ 0xa0, 0xa7, 0x6d, 0x73, 0x8a, 0x9d, 0x16, 0x7d, 0x05, 0x2d, 0xd6, 0x6b, 0xf4, 0x8d, 0x4b, 0x81,
+ 0x98, 0x2f, 0x46, 0xa5, 0x34, 0x84, 0xf3, 0x70, 0xa4, 0xe9, 0x04, 0x84, 0x7b, 0x87, 0x79, 0x3c,
+ 0x01, 0x25, 0xb5, 0xfc, 0x3d, 0xd0, 0x25, 0xea, 0x2f, 0x91, 0xf0, 0x3f, 0x7f, 0xd4, 0x8e, 0x1e,
+ 0x36, 0x83, 0x22, 0x91, 0x57, 0x92, 0x36, 0x0b, 0x44, 0xa5, 0xcc, 0x5e, 0xef, 0x44, 0x3e, 0xf8,
+ 0xe9, 0x7b, 0x5e, 0x0c, 0xea, 0xb2, 0x50, 0x39, 0xb7, 0xea, 0xc4, 0xfb, 0xe4, 0x37, 0xf8, 0x85,
+ 0xc2, 0xdc, 0x01, 0x98, 0x01, 0x2a, 0x44, 0xd3, 0x75, 0x10, 0x38, 0xf4, 0x85, 0x3e, 0xc9, 0xf7,
+ 0xe7, 0xe4, 0xec, 0x40, 0x3d, 0x8f, 0xa5, 0xd2, 0x8a, 0xca, 0x62, 0x03, 0x3f, 0x65, 0x28, 0x8d,
+ 0xf5, 0x56, 0xa7, 0xea, 0xd1, 0x0d, 0x70, 0x82, 0xbc, 0x90, 0x59, 0xf8, 0x3e, 0x08, 0xc9, 0xe1,
+ 0x97, 0xef, 0x82, 0x43, 0x35, 0x41, 0x3e, 0x7f, 0x00, 0x96, 0x3f, 0x90, 0xe5, 0x1e, 0x96, 0xba,
+ 0xce, 0x6d, 0xd2, 0x54, 0xce, 0x84, 0x76, 0x3c
+};
+
+
+////////// odd key, only 80 (0x50) bytes of payload (10 groups of 8 bytes + 0 byte residue)
+unsigned char test_p_10_0_key[0x8] = {
+ 0x2d, 0x11, 0x5f, 0x9d, 0x29, 0xbf, 0x7f, 0x67
+};
+unsigned char test_p_10_0_encrypted[0x100] = {
+ 0x47, 0x00, 0x7a, 0xbe,
+ 0x67, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x71, 0xa5, 0x7b, 0x8f, 0xf9, 0x87, 0xcb, 0xac,
+ 0xea, 0x08, 0x0c, 0x02, 0x87, 0x7b, 0xad, 0x10, 0x40, 0x28, 0x8e, 0xd4, 0x4e, 0x62, 0xc7, 0x74,
+ 0xd6, 0xbb, 0x3a, 0xaa, 0xb0, 0x7b, 0x70, 0xbe, 0x06, 0xc9, 0xdc, 0x07, 0xd2, 0x2d, 0xab, 0x2d,
+ 0xe2, 0xc6, 0x36, 0xa6, 0xda, 0x64, 0x61, 0x15, 0xd1, 0x6a, 0x40, 0xc0, 0xa9, 0xfb, 0x3f, 0xb2,
+ 0x6d, 0xa5, 0x59, 0xae, 0x57, 0x88, 0x6b, 0x0e, 0x00, 0xae, 0xce, 0x64, 0xee, 0xfd, 0xb1, 0x7f,
+ 0x78, 0x9c, 0x12, 0x42, 0xbe, 0x30, 0x8a, 0xa3
+};
+unsigned char test_p_10_0_expected[0x100] = {
+ 0x47, 0x00, 0x7a, 0xbe,
+ 0x67, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa7, 0xca, 0x32, 0xaf, 0x2e, 0x6a, 0xea, 0x05,
+ 0x39, 0x33, 0x67, 0x5d, 0xa3, 0x61, 0x0f, 0x34, 0x40, 0x6c, 0x1a, 0xb3, 0xee, 0x54, 0x64, 0xd5,
+ 0xa3, 0x01, 0x95, 0x87, 0x9d, 0x3d, 0x38, 0xc5, 0x82, 0x8b, 0x8d, 0xab, 0xad, 0x93, 0x0f, 0xe8,
+ 0xf9, 0xbd, 0x52, 0x98, 0x59, 0xb2, 0x41, 0x95, 0xcd, 0xae, 0x9b, 0x3e, 0xdf, 0xdb, 0x14, 0x9b,
+ 0xa9, 0x22, 0x0d, 0x2d, 0x61, 0xf5, 0xf2, 0x52, 0x83, 0x20, 0xae, 0xb8, 0x83, 0x52, 0x02, 0xee,
+ 0xbd, 0xd2, 0x94, 0x6c, 0x27, 0x58, 0x55, 0xd0
+};
+
+
+////////// odd key, only 14 (0x0e) bytes of payload (1 group of 8 bytes + 6 byte residue)
+unsigned char test_p_1_6_key[0x8] = {
+ 0x2d, 0x11, 0x5f, 0x9d, 0x29, 0xbf, 0x7f, 0x67
+};
+unsigned char test_p_1_6_encrypted[0x100] = {
+ 0x47, 0x00, 0x7a, 0xb7,
+ 0xa9, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc0, 0x5e, 0xfb, 0xc8, 0x4a, 0x63,
+ 0xe3, 0x3c, 0x11, 0xd9, 0xe0, 0x75, 0x8e, 0xf2
+};
+unsigned char test_p_1_6_expected[0x100] = {
+ 0x47, 0x00, 0x7a, 0xb7,
+ 0xa9, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5a, 0x2c, 0xee, 0xb3, 0xde, 0x92,
+ 0xe7, 0xa6, 0x6c, 0xaa, 0x99, 0x84, 0xe4, 0x00
+};
--- /dev/null
+##### compiling with g++ gives a little more speed
+#COMPILER=gcc
+#COMPILER=g++
+
+###there are two functions which apparently don't want to be inlined
+#FLAGS=-O3 -march=athlon-xp -fexpensive-optimizations -funroll-loops -finline-limit=6000000 --param max-unrolled-insns=500
+#FLAGS=-O3 -march=athlon-xp -fexpensive-optimizations -funroll-loops --param max-unrolled-insns=500
+#FLAGS=-O3 -march=pentium3 -fexpensive-optimizations -funroll-loops
+
+###icc crashes for unknown reasons
+#COMPILER=/opt/intel_cc_80/bin/icc
+#FLAGS=-O3 -march=pentiumiii
+
+#FLAGS += -g
+#FLAGS += -fno-alias
+#FLAGS += -vec_report3
+#FLAGS += -Wall -Winline
+#FLAGS += -fomit-frame-pointer
+#FLAGS += -pg
+
+COMPILER ?= g++
+FLAGS ?= -Wall -fPIC -O3 -march=pentium -mmmx -fomit-frame-pointer -fexpensive-optimizations -funroll-loops
+
+H_FILES = FFdecsa.h parallel_generic.h parallel_std_def.h fftable.h \
+ parallel_032_4char.h \
+ parallel_032_int.h \
+ parallel_064_2int.h \
+ parallel_064_8charA.h \
+ parallel_064_8char.h \
+ parallel_064_long.h \
+ parallel_064_mmx.h \
+ parallel_128_16charA.h \
+ parallel_128_16char.h \
+ parallel_128_2long.h \
+ parallel_128_2mmx.h \
+ parallel_128_4int.h \
+ parallel_128_sse2.h \
+ parallel_128_sse.h
+
+all: FFdecsa.o
+
+%.o: %.c
+ $(COMPILER) $(FLAGS) -c $<
+
+FFdecsa_test: FFdecsa_test.o FFdecsa.o
+ $(COMPILER) $(FLAGS) -o FFdecsa_test FFdecsa_test.o FFdecsa.o
+
+FFdecsa_test.o: FFdecsa_test.c FFdecsa.h FFdecsa_test_testcases.h
+FFdecsa.o: FFdecsa.c stream.c $(H_FILES)
+
+clean:
+ @rm -f FFdecsa_test FFdecsa_test.o FFdecsa.o
+
+test: FFdecsa_test
+ sync;usleep 200000;nice --19 ./FFdecsa_test
--- /dev/null
+-------
+FFdecsa
+-------
+version 1.0
+Copyright 2003-2004 fatih89r
+released under GPL
+
+
+FFdecsa is a fast implementation of a CSA decryption algorithm for MPEG
+TS packets. It is shockingly fast, more than 800% the speed of the
+fastest implementation I can find around. (read the docs to know what FF
+stands for)
+
+On an AthlonXP 2400 (2000MHz) it achieves 165Mbit/s; the previous record
+was around 20Mbit/s.
+
+This means that:
+- decrypting a 8Mbit/s stream takes 5% of CPU instead of 40%
+- decrypting a full transponder (with all its channels or with a big
+ HDTV stream) carrying 38Mbit/s takes 23% of CPU instead of 190%
+ (>100%, so undecryptable in real time)
+- a very slow processor can decrypt one channel with no problems
+- offline decoding of one hour of a 5Mbit/s channel takes less than
+ two minutes (30x than realtime)
+- offline decoding will work at more than 20MB/s (megabytes/s),
+ nearly as fast as a file copy
+
+The docs directory contains useful stuff:
+
+ FAQ.txt
+ to know something more about this software
+
+ how_to_compile.txt
+ if you want to compile this code (and get optimal speed)
+
+ how_to_use.txt
+ if you want to use this code
+
+ technical_background.txt
+ if you want to understand how this code works or you want to
+ modify/improve it
+
+ how_to_understand.txt
+ if you want to understand the code to make modifications
+
+ how_to_release.txt
+ if you want to release modified versions of the code
+
+
+fatih89r
--- /dev/null
+-------
+FFdecsa
+-------
+
+FFdecsa is a fast implementation of the CSA decryption algorithm for MPEG
+TS packets.
+
+Q: What does FF stands for?
+A: FFdecsa means "Fucking Fast decsa".
+
+Q: Why would you use such a rude name?
+A: Because this code is fucking fast, more than 800% the speed of the best
+ implementation I'm able to find around at the moment.
+
+Q: How it that possible? Are all other programmers stupid?
+A: No, they just tried to save a cycle or two tweaking a fundamentally wrong
+ implementation. The algorithm has to be implemented in a totally different
+ way to achieve good speed.
+
+Q: Do you use multimedia instructions?
+A: I use every trick I could come up with, including multimedia instructions.
+ They are not fundamental in achieving speed, a version without them runs
+ at 6x the speed of the best implementation around (which uses MMX).
+
+Q: So how did you do that?
+A: By using a different approach for the implementation. This code is not
+ exploiting some new CSA vulnerability, it is just doing the same
+ calculations better. Think about replacing bubble sort with quick sort.
+
+Q: You're joking, it's impossible to gain so much speed.
+A: Speed test are available, technical documentation is available, source
+ code is available. Try it yourself.
+ If you want details, these are some of the documented tricks I used
+ (more details in the docs directory):
+ TRICK NUMBER 0: emulate the hardware
+ TRICK NUMBER 1: virtual shift registers
+ TRICK NUMBER 2: parallel bitslice
+ TRICK NUMBER 3: multimedia instructions
+ TRICK NUMBER 4: parallel byteslice
+ TRICK NUMBER 5: efficient bit permutation
+ TRICK NUMBER 6: efficient normal<->slice conversion
+ TRICK NUMBER 7: try hard to process packets together
+ TRICK NUMBER 8: try to avoid doing the same thing many times
+ TRICK NUMBER 9: compiler
+ TRICK NUMBER a: a lot of brain work
+
+Q: How can be this code useful?
+A: You can use this code in place of the old slow implementations and save a
+ lot of CPU power.
+
+Q: Just that?
+A: Well, new applications are possible.
+ Decrypting a whole transponder is easily doable now. Well, a $50 CPU can
+ decrypt four transponder at the same time if you have four DVB boards (but
+ I couldn't test that).
+
+Q: You're cheating, this code is fake, I don't believe one word.
+A: Go away. This is technical stuff for people with brains.
+
+Q: This code is great, may I distribute your code in original or modified
+ form?
+A: Only if you respect the license.
+
+Q: May I use your code in my player/library/plugin...?
+A: Again, you have to respect the license.
+
+Q: Are you an extraterrestrial programmer?
+A: No, just a Turkish guy with a PC to play with :-)
+
+Q: Why did you spend your time doing this?
+A: Because I thought that my approach was doable and I was sure it would
+ have been much faster, so I had to implement it to confirm I was right.
+ I got 8x the speed and that's enough to be proud of it. And I could not
+ just keep the code for myself only.
+
+Q: What is the answer to the meaning of the universe?
+A: 42,43,71,5f,65,85,f6,76,0d,13,28,96,...
--- /dev/null
+-------
+FFdecsa
+-------
+
+Compiling is as easy as running a make command, if you have gcc and are
+using a little endian machine. 64 bit machines have not been tested but
+may work with little or no changes; big endian machines will certainly
+give incorrect results (read the technical_background.txt to know where
+the problem is).
+
+Before compiling you could edit the Makefile to tweak compiler flags for
+optimal performance. If you want to play with different bit-grouping
+strategies you have to edit FFdecsa_DBG.c and change the "our choice"
+definition. This is highly critical for performance.
+
+After compilation run the FFdecsa_test application. It will test correct
+decryption and print the meausered speed (use "nice --19 ./FFdecsa_test"
+on an idle machine for better results). Or just use "make test".
+
+gcc >=3.3.3 is highly recommended. Older versions could give performance
+problems.
+
+icc is currently unusable. In the initial phases of development of
+FFdecsa icc was able to compile the code and gave interesting speed
+results when using the 8charA grouping mode (array of 8 characters are
+automatically manipulated through MMX instructions). At some point the
+code began to work incorrectly because of a compiler bug (but I found a
+workaround). Then, the performance dropped with no reason; I found a
+workaround by adding an unused variable (alignment problem, grep for icc
+in the code to see where it happens). Then, with the introduction of
+group modes based on intrinsics, gcc was finally able to go beyond the
+speed record originally set by icc. Additional code tweaks added more
+speed to gcc, while icc started to segfault on compilation (both version
+7 and 8). In conclusion, icc is bugged and this code is too hard for it.
+gcc on the other hand is great. I tried to inspect generated assembler
+to find weak spots, and the generated code is very good indeed.
+
+Note: the code can be compiled with gcc or g++. g++ is 3% faster for
+some reason.
+
+You should not get any errors or warnings. I only get two "inlining
+failed" warnings on two functions I asked to be inlined but gcc doesn't
+want to inline.
+
+The build process creates additional temp files by running grep
+commands. This is how debugging output is handled. All the lines
+containing DBG are removed and the temp file is compiled (so the line
+numbers change between temp and original files). Don't edit the temp
+files, they will be overwritten. If you don't remove the DBG lines (for
+example, by changing "grep -v DBG" into "grep -v aaDBG" in Makefile) a
+lot of output will be generated. This is useful to understand what's
+wrong when the FFdecsa_test is failing. I included a reference "known
+good" output in the debug_output directory. Extra debug output is
+commented out in the code.
+
+The debug output functionality could be... bugged. This is because I
+tested everything using hard coded int grouping mode and then
+generalized the debug output to abstract grouping modes. A bug where 4
+bytes are printed instead of 8 could be present somewhere. I think it
+isn't, but you've been warned.
+
+This code was only tried on Linux.
+It should work on Windows or other platforms, but you may encounter
+problems related to the compiler quality. If you want to try, begin with
+the int grouping mode. It is only 30% slower then the best (MMX) and it
+should be easily portable because no intrinsics are used. I'm
+particularly interested in hearing what kind of performance can be
+obtained on x86_64 processors in int, long long int, mmx, 2mmx, sse
+modes.
+
+
+As a reference, here are the results I get on an Athlon XP 2400+ (this
+processor runs at 2000MHz); other processors belonging to the Athlon XP
+architecture, including Durons, should have the same speed per MHz.
+Cache size and bus speed don't matter.
+
+CPU: AMD Athlon XP 2400+
+
+Compiler: g++ (gcc version 3.3.3 20040412 (Red Hat Linux 3.3.3-7))
+
+Flags: -O3 -march=athlon-xp -fexpensive-optimizations -funroll-loops
+ --param max-unrolled-insns=500
+
+grouping mode speed (Mbit/s) notes
+---------------------------------------------------------------------
+PARALLEL_32_4CHAR 14
+PARALLEL_32_4CHARA 12
+PARALLEL_32_INT 125 very good and very portable
+PARALLEL_64_8CHAR 17
+PARALLEL_64_8CHARA 15 needs a vectorizing compiler
+PARALLEL_64_2INT 75 x86 has too few registers
+PARALLEL_64_LONG 97 try this on x86_64
+PARALLEL_64_MMX 165 the best
+PARALLEL_128_16CHAR 6
+PARALLEL_128_16CHARA 7
+PARALLEL_128_4INT 69
+PARALLEL_128_2LONG 52
+PARALLEL_128_2MMX 36 slower than expected
+PARALLEL_128_SSE 156 just slower than 64_MMX
+
+Best speeds are obtained with native data types: int, mmx, sse (this
+could be a compiler artifact).
+
+64 bit processors should try 64_LONG.
+
+Vectorizing compilers should like *CHARA.
+
+64_MMX is faster than 128_SSE on the Athlon; perhaps SSE instruction are
+internally split into 64 bit chunks. Could be different on x86_64 or
+Intel processors.
+
+128_SSE has a 64 bit (MMX) batch type because SSE has no shifting
+instructions, they are only available on SSE2. As the Athlon XP doesn't
+support SSE2, I couldn't experiment with that.
--- /dev/null
+-------
+FFdecsa
+-------
+
+Please use the name of the release you're basing on as a base name and
+add your suffix.
+
+For example if john modifies
+ FFdecsa-1.0.0
+he should release
+ FFdecsa-1.0.0-john_0.3
+or
+ FFdecsa-1.0.0-john_0.4
+
+If paul modifies john's version the correct name would be like
+ FFdecsa-1.0.0-john_0.4-paul_0.1
+
+This is to avoid many different versions with random version numbers, as
+development is not centralized.
+
+Thank you.
--- /dev/null
+-------
+FFdecsa
+-------
+
+First, you need to know how decsa works, study the source of a classical
+implementation. Then you have to understand how things are done in
+slicing mode. Read all the documentation and have a working classical
+implementation to compare partial results. There are comments spread
+around the code. Some things are difficult to understand without paper
+notes; for example the matrix transpositions and meaning of array
+indices.
+
+Sorry, it is hard to understand and modify ...
+
+... but it was harder to design and implement!!!
--- /dev/null
+-------
+FFdecsa
+-------
+
+This code is able to decrypt MPEG TS packets with the CSA algorithm. To
+achieve high speed, the decryption core works on many packets at the
+same time, so the interface is more complicated than usual decsa
+implementations.
+
+The FFdecsa.h file defines the external interface of this code.
+
+Basically:
+
+1) you use get_suggested_cluster_size to know the optimal number of
+packets you have to pass for decryption
+
+2) you use set_control_words to set the decryption keys
+
+3) you use decrypt_packets to do the actual decryption
+
+You don't need to always use set_control_words before decrypt_packets,
+if keys aren't changed.
+
+
+The decrypt_packets function call decrypts many packets at the same
+time. The interface is complicated because the only design goal was
+speed, so it implements zero-copying of packets, out-of-order decryption
+and optimal packet aggregation for better parallelism. This part is the
+most difficult to understand.
+
+--- HOW TO USE int decrypt_packets(unsigned char **cluster); ---
+
+PARAMETERS
+ cluster points to an array of pointers, representing zero or more
+ ranges. Every range has a start and end pointer; a start pointer==NULL
+ terminates the array.
+ So, an array of pointers has this content:
+ start_of_buffer_1, end_of_buffer_1, ... start_of_buffer_N,
+ end_of_buffer_N, NULL
+ example:
+ 0x12340000, 0x123400bc, 0x56780a00, 0x5678b78, NULL
+ has two ranges (0x12340000 - 0x123400bc and 0x56780a00 - 0x5678b78),
+ for a total of three packets (starting at 0x12340000, 0x56780a00,
+ 0x5678abc)
+RETURNS
+ How many packets can now be consumed by the caller, this is always >=
+ 1, unless the cluster contained zero packets (in that case it's
+ obviously zero).
+MODIFIES
+ The cluster is modified to try to exclude packets which shouldn't be
+ submitted again for decryption (because just decrypted or originally
+ not crypted). "Try to exclude" because the returned array will never
+ be bigger than what was passed, so if you passed only a range and some
+ packets in the middle were decrypted making "holes" into the range,
+ the range would have to be split into several ranges, and that will
+ not be done. If you want a strict description of what has to be passed
+ again to decrypt_packets, you have to use ranges with only one packet
+ inside. Note that the first packet will certainly be eliminated from
+ the returned cluster (see also RETURNS).
+
+You can now read the detailed description of operation or just skip to
+the API examples.
+
+
+---------------------------------
+DETAILED DESCRIPTION OF OPERATION
+---------------------------------
+ consider a sequence of packets like this:
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+ E E E E E E E E E E E O E O E O O 0 0 0 0 0 0 0 0 c O O O O O O O O O O O ...
+ where
+ E = encrypted_even,
+ O = encrypted_odd,
+ e = clear_was_encrypted_even,
+ o = clear_was_encrypted_odd,
+ c = clear
+ and suppose the suggested cluster size is 10 (this could be for a function with internal parallelism 8)
+
+ 1) we define the cluster to include packets 0-9 and
+ call decrypt_packets
+ a possible result is that the function call
+ - returns 8 (8 packets available)
+ - the buffer contains now this
+ -----------------------------
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+ e e e e e e e e E E E O E O E O O 0 0 0 0 0 0 0 0 c O O O O O O O O O O O ...
+ -----
+ - the modified cluster covers 8-9 [continue reading, but then see note 1 below]
+ so, we can use the first 8 packets of the original cluster (0-7)
+
+ 2) now, we define cluster over 8-17 and call decrypt_packets
+ a possible result is:
+ - returns 3 (3 packets available)
+ - the buffer contains now this (!!!)
+ -----------------------------
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+ e e e e e e e e e e e O e O e O O 0 0 0 0 0 0 0 0 c O O O O O O O O O O O ...
+ -- -- --------
+ - the modified cluster covers 11-11,13-13,15-17 [continue reading, but then see note 1 below]
+ so, we can use the first 3 packets of the original cluster (8-10)
+
+ 3) now, we define cluster over 11-20 and call decrypt packets (defining a cluster 11-11,13-13,15-22 would be better)
+ a possible result is:
+ - returns 10 (10 packets available)
+ - the buffer contains now this
+ -----------------------------
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+ e e e e e e e e e e e o e o e o o o o o o 0 0 0 0 c O O O O O O O O O O O ...
+
+ - the modified cluster is empty
+ so, we can use the first 10 packets of the original cluster (11-20)
+ What it happened is that the second call decrypted packets 12 and 14 but they were
+ not made available because packet 11 was still encrypted,
+ the third call decrypted 11,13,15-20 and included 12 and 14 as available too.
+
+ 4) now, we define cluster over 21-30 and call decrypt packets
+ a possible result is:
+ - returns 9 (9 packets available)
+ - the buffer contains now this
+ -----------------------------
+ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 ...
+ e e e e e e e e e e e o e o e o o o o o o o o o o c o o o o O O O O O O O ...
+ --
+ - the modified cluster covers 30-30
+ so, we can use the first 9 packets of the original cluster (21-29)
+ What happened is that packet 25 could be skipped because it is in clear.
+
+ Note that the suggested cluster size (10) is higher than the maximum number
+ of packets that can be really decrypted (8), but we are able to skip 12 and 14
+ in step 3) and run the decryption on a full 8 packets group.
+ In the same way, we were able to skip 25 in step 4).
+ There are three kinds of "free" packets we can skip:
+ - packets decrypted in a previous call (as 12 and 14)
+ - packets already in clear (as 25)
+ - packets with a payload of less than 8 bytes (clear==encrypted!)
+
+ Note also that we could have defined a better cluster in step 3
+ (11-11,13-13,15-22), using what step 2 had returned. The risk of not
+ having 8 packets to decrypt would have been smaller (consider the case
+ where 19 and 20 were "c").
+
+ Final considerations:
+ - you can use a bigger or smaller cluster than the suggested number of packets
+ - every call to decrypt_packets has a *fixed* CPU cost, so you should try to
+ not run it with a few packets, when possible
+ - decrypt_packets can't decrypt even and odd at the same time; it guarantees
+ that the first packet will be decrypted and tries to decrypt as many packets
+ as possible
+ - clear packets in the middle of encrypted packets don't happen in real world,
+ but E,E,E,O,E,O,O,O sequences do happen (audio/video muxing problems?) and
+ small packets (<8 bytes) happen frequently; the ability to skip is useful.
+
+ note 1:
+ As the returned cluster will not have more ranges than the passed one, what it is
+ described above is not actually true.
+ In the step 1) the returned cluster will cover 8-9, but in step 2) it will
+ cover 11-17 (some extra packets had to remain in); this lack of information
+ prevents us from using an optimal 11-11,13-13,15-22 in step 3). Note that
+ in any case step 3) will decrypt 11,13,15,16,17,18,19,20 thanks to the
+ extra margin we use (we put ten packets (including 19 and 20) even if the
+ parallelism was just 8, and it was a good idea; but if 19 and 20 were of
+ type c, we would have run the decryption with only 6/8 efficiency).
+ This problem can be prevented by using ranges with only one packet: in
+ step 2) we would have passed
+ 8-8,9-9,10-10,11-11,12-12,13-13,14-14,15-15,16-16,17-17
+ and got back
+ 11-11,13-13,15-17.
+
+
+------------
+API EXAMPLES
+------------
+
+Some examples of how the API can be used (this is not real code, so it
+may have typos or other bugs).
+
+
+Example 1: (big linear buffer, simple use of cluster)
+
+ unsigned char *p;
+ unsigned char *cluster[3];
+ for(p=start;p<end;){
+ cluster[0]=p;cluster[1]=end;
+ cluster[2]=NULL;
+ p+=188*decrypt_packets(cluster);
+ }
+ //consume(start,end);
+
+
+Example 2: (circular buffer, simple use of cluster)
+
+ unsigned char *p;
+ unsigned char *cluster[5];
+
+ while(1){
+ if(read==write){
+ //buffer is empty
+ //write=refill_buffer(write,start,end);
+ continue;
+ }
+ else if(read<write){
+ cluster[0]=read;cluster[1]=write;
+ cluster[2]=NULL;
+ }
+ else{
+ cluster[0]=read;cluster[1]=end;
+ cluster[2]=start;cluster[3]=write;
+ cluster[4]=NULL;
+ }
+ new_read=read+188*decrypt_packets(cluster);
+ if(new_read<=end){
+ //consume(read,new_read);
+ }
+ else{
+ new_read=start+(new_read-end);
+ //consume(read,end);
+ //consume(start,new_read);
+ }
+ read=new_read;
+ if(read==end) read=start;
+ }
+
+
+Example 3: (undefined buffer structure, advanced use of cluster)
+
+ unsigned char *packets[1000000];
+ unsigned char *cluster[142]; //if suggested packets is 70
+
+ cluster[0]=NULL;
+ for(n=0;n<1000000;){
+ i=0;
+ while(cluster[2*i]!=NULL) i++; //preserve returned ranges
+ for(k=i;k<70&&n<1000000;k++,n++){
+ cluster[2*k]=packets[n];cluster[2*k+1]=packets[n]+188;
+ }
+ cluster[2*k]=NULL;
+ decrypt_packets(cluster);
+ }
+ //consume_all_packets();
--- /dev/null
+-------
+FFdecsa
+-------
+
+This doc is for people who looked into the source code and found it
+difficult to believe that this is a decsa algorithm, as it appears
+completely different from other decsa implementations.
+
+It appears different because it is different. Being different is what
+enables it to be a lot faster than all the others (currently it has more
+than 800% the speed of the best version I was able to find)
+
+The csa algo was designed to be run in hardware, but people are now
+running it in software.
+
+Hardware has data lines carrying bits and functional blocks doing
+calculations (logic operations, adders, shifters, table lookup, ...),
+software instead uses memory to contain data values and executes a
+sequence of instructions to transform the values. As a consequence,
+writing a software implementation of a hardware algorithm can be
+inefficient.
+
+For example, if you have 32 data lines, you can permutate the bits with
+zero cost in hardware (you just permute the physical traces), but if you
+have the bits in a 32 bit variable you have to use 32 "and" operations
+with 32 different masks, 32 shifts and 31 "or" operations (if you
+suggest using "if"s testing the bits one by one you know nothing about
+how jump prediction works in modern processors).
+
+So the approach is *emulating the hardware*.
+
+Then there are some additional cool tricks.
+
+TRICK NUMBER 0: emulate the hardware
+------------------------------------
+We will work on bits one by one, that is a 4 bit word is now four
+variables. In this way we revert complex software operations into
+hardware emulation:
+
+ software hardware
+ -------------------------------------------
+ copy values copy values
+ logic op logic op
+ (bit permut.) ands+shifts+ors copy values
+ additions logic op emulating adders
+ (comparisons) if logic op selecting one of the two results
+ lookup tables logic op synthetizing a ROM (*)
+
+(*) sometimes lookup tables can be converted to logic expressions
+
+The sbox in the stream cypher have been converted to efficient logic
+operations using a custom written software (look into logic directory)
+and is responsible for a lot of speed increase. Maybe there exists a
+slightly better way to express the sbox as logical expressions, but it
+would be a minuscule improvement. The sbox in the block cypher can't be
+converted to efficient logic operations (8 bits of inputs are just too
+much) and is implemeted with a traditional lookup in an array.
+
+But there is a problem; if we want to process bits, but our external
+input and output wants bytes. We need conversion routines. Conversion
+routines are similar to the awful permutations we described before, so
+this has to be done efficiently someway.
+
+
+TRICK NUMBER 1: virtual shift registers
+---------------------------------------
+Shift registers are normally implemented by moving all data around.
+Better leave the data in the same memory locations and redefine where
+the start of the register is (updating a pointer). That is called
+virtual shift register.
+
+
+TRICK NUMBER 2: parallel bitslice
+---------------------------------
+Implementing the algorithm as described in tricks 1 and 2 give us about
+15% of the speed of a traditional implementation. This happens because
+we work on only one bit, even if our CPU is 32 bit wide. But *we can
+process 32 different packets at the same time*. This is called
+"bitslice" method. It can be done only if the program flow is not
+dependent of the data (if, while,...). Luckily this is true.
+Things like
+ if(a){
+ b=c&d;
+ }
+ else{
+ b=e&f;
+ }
+can be coded as (think of how hardware would implement this)
+ b1=c&d;
+ b2=e&f;
+ b=b2^(a&(b1^b2));
+and things like
+ if(a){
+ b=c&d
+ }
+can be transformed in the same way, as they may be written as
+ if(a){
+ b=c&d
+ }
+ else{
+ b=b;
+ }
+It could look wasteful, but it is not; and destroys data dependency.
+
+Our codes takes the same time as before, but produces 32 results, so
+speed is now 480% the speed of a traditional implementation.
+
+
+TRICK NUMBER 3: multimedia instructions
+---------------------------------------
+If our CPU is 32 bit but it can also process larger blocks of data
+efficiently (multimedia instructions), we can use them. We only need
+logic ops and these are typically available.
+
+We can use MMX and work on 64 packets, or SSE and work on 128 packets.
+The speed doesn't automatically double going from 32 to 64 because the
+integer registers of the processor are normally faster. However, some
+speed is gained in this way.
+
+Multimedia instructions are often used by writing assembler by hand, but
+compilers are very good in doing register allocation, loop unrolling and
+instruction scheduling, so it is better to write the code in C and use
+native multimedia data types (intrinsics).
+
+Depending on number of available registers, execution latency, number of
+execution units in the CPU, it may be good to process more than one data
+block at the same time, for example 2 64bit MMX values. In this case we
+work on 128 bits by simulating a 128 bit op with two consecutive 64 bit
+op. This may or may not help (apparently not because x86 architecture
+has a small number of registers).
+
+We can also try working on 96 bit, pairing a MMX and an int op, or 192
+bit by using MMX and SSE. While this is doable in theory and could
+exploit different execution units in the CPU, speed doesn't improve
+(because of cache line handling problems inside the CPU, maybe).
+
+Besides int, MMX, SSE, we can use long long int (64 bit) and, why not,
+unsigned char.
+
+Using groups of unsigned chars (8 or 16) could give the compiler an
+opportunity to insert multimedia instructions automatically. For
+example, icc can use one MMX istruction to do
+ unsigned char a[8],b[8],c[8];
+ for(i=0;i<8;i++){
+ a[i]=b[i]&c[i];
+ }
+Some compilers (like icc) are efficient in this case, but using
+intrinsics manually is generally faster.
+
+All these experiments can be easily done if the code is written in a way
+which abstracts the data type used. This is not easy but doable, all the
+operations on data become (inlined) function calls or preprocessor
+macros. Good compilers are able to simplify all the abstraction at
+compile time and generate perfect code (gcc is great).
+
+The data abstraction used in the code is called "group".
+
+
+TRICK NUMBER 4: parallel byteslice
+----------------------------------
+The bitslice method works wonderfully on the stream cypher, but can't be
+applied to the block cypher because of the evil big look up table.
+
+As we have to convert input data from normal to bitslice before starting
+processing and from bitslice to normal before output, we convert the
+stream cypher output to normal before the block calculations and do the
+block stage in a traditional way.
+
+There are some xors in the block cypher; so we arrange bytes from
+different packets side by side and use multimedia instructions to work
+on many bytes at the same time. This is not exactly bitslice, maybe it
+is called byteslice. The conversion routines are similar (just a bit
+simpler).
+
+The data type we use to do this in the code is called "batch".
+
+The virtual shift register described in trick number 2 is useful too.
+
+The look up table is the only thing which is done serially one byte at a
+time. Luckily if we do it on 32 or 64 bytes the loop is heavily
+unrolled, and the compiler and the CPU manage to get a good speed
+because there is little dependency between instructions.
+
+
+TRICK NUMBER 5: efficient bit permutation
+-----------------------------------------
+The block cypher has a bit permutation part. As we are not in a bit
+sliced form at that point, permuting bits in a byte takes 8 masks, 8
+and, 7 or; but three bits move in the same direction, so we make it with
+6 masks, 6 and, 5 or. Batch processing through multimedia instructions
+is applicable too.
+
+
+TRICK NUMBER 6: efficient normal<->slice conversion
+---------------------------------------------------
+The bitslice<->normal conversion routines are a sort of transposition
+operation, that is you have bits in rows and want them in columns. This
+can be done efficiently. For example, transposition of 8 bytes (matrix
+of 8x8=64 bits) can be done this way (we want to exchange bit[i][j] with
+bit[j][i] and we assume bit 0 is the MSB in the byte):
+
+ // untested code, may be bugged
+ unsigned char a[8];
+ unsigned char b[8];
+ for(i=0;i<8;i++) b[i]=0;
+ for(i=0;i<8;i++){
+ for(j=0;j<8;j++){
+ b[i]|=((a[j]>>(7-i)&1))<<(7-j);
+ }
+ }
+
+but it is slow (128 shifts, 64 and, 64 or), or
+
+ // untested code, may be bugged
+ unsigned char a[8];
+ unsigned char b[8];
+ for(i=0;i<8;i++) b[i]=0;
+ for(i=0;i<8;i++){
+ for(j=0;j<8;j++){
+ if(a[j]&(1<<(7-i))) b[i]|=1<<(7-j);
+ }
+ }
+
+but is very very slow (128 shifts, 64 and, 64 or, 128 unpredictable
+if!), or using a>>=1 and b<<=1, which gains you nothing, or
+
+ // untested code, may be bugged
+ unsigned char a[8];
+ unsigned char b[8];
+ unsigned char top,bottom;
+ for(j=0;j<1;j++){
+ for(i=0;i<4;i++){
+ top= a[8*j+i];
+ bottom=a[8*j+4+i];
+ a[8*j+i]= (top&0xf0) |((bottom&0xf0)>>4);
+ a[8*j+4+i]=((top&0x0f)<<4)| (bottom&0x0f);
+ }
+ }
+ for(j=0;j<2;j++){
+ for(i=0;i<2;i++){
+ top= a[4*j+i];
+ bottom=a[4*j+2+i];
+ a[4*j+i] = (top&0xcc) |((bottom&0xcc)>>2);
+ a[4*j+2+i]=((top&0x33)<<2)| (bottom&0x33);
+ }
+ }
+ for(j=0;j<4;j++){
+ for(i=0;i<1;i++){
+ top= a[2*j+i];
+ bottom=a[2*j+1+i];
+ a[2*j+i] = (top&0xaa) |((bottom&0xaa)>>1);
+ a[2*j+1+i]=((top&0x55)<<1)| (bottom&0x55);
+ }
+ }
+ for(i=0;i<8;i++) b[i]=a[i]; //easy to integrate into one of the stages above
+
+which is very fast (24 shifts, 48 and, 24 or) and has redundant loops
+and address calculations which will be optimized away by the compiler.
+It can be written as 3 nested loops but it becomes less readable and
+makes it difficult to have results in b without an extra copy. The
+compiler always unrolls heavily.
+
+The gain is much bigger when operating with 32 bit or 64 bit values (we
+are going from N^2 to Nlog(N)). This method is used for rectangular
+matrixes too (they have to be seen as square matrixes side by side).
+Warning: this code is not *endian independent* if you use ints to work
+on 4 bytes. Running it on a big endian processor will give you a
+different and strange kind of bit rotation if you don't modify masks and
+shifts.
+
+This is done in the code using int or long long int. It should be
+possible to use MMX instead of long long int and it could be faster, but
+this code doesn't cost a great fraction of the total time. There are
+problems with the shifts, as multimedia instructions do not have all
+possible kind of shift we need (SSE has none!).
+
+
+TRICK NUMBER 7: try hard to process packets together
+----------------------------------------------------
+As we are able to process many packets together, we have to avoid
+running with many slots empty. Processing one packet or 64 packets takes
+the same time if the internal parallelism is 64! So we try hard to
+aggregate packets that can be processed together; for simplicity reasons
+we don't mix packets with even and odd parity (different keys), even if
+it should be doable with a little effort. Sometimes the transition from
+even to odd parity and viceversa is not sharp, but there are sequences
+like EEEEEOEEOEEOOOO. We try to group all the E together even if there
+are O between them. This out-of-order processing complicates the
+interface to the applications a bit but saves us three or four runs with
+many empty slots.
+
+We have also logic to process together packets with a different size of
+the payload, which is not always 184 bytes. This involves sorting the
+packets by size before processing and careful operation of the 23
+iteration loop to exclude some packets from the calculations. It is not
+CPU heavy.
+
+Packets with payload <8 bytes are identical before and after decryption
+(!), so we skip them without using a slot. (according to DVB specs these
+kind of packets shouldn't happen, but they are used in the real world).
+
+
+TRICK NUMBER 8: try to avoid doing the same thing many times
+------------------------------------------------------------
+Some calculations related to keys are only done when the keys are set,
+then all the values depending on keys are stored in a convenient form
+and used everytime we convert a group of packets.
+
+
+TRICK NUMBER 9: compiler
+------------------------
+
+Compilers have a lot of optimization options. I used -march to target my
+CPU and played with unsual options. In particular
+ "--param max-unrolled-insns=500"
+does a good job on the tricky table lookup in the block cypher. Bigger
+values unroll too much somewhere and loose speed. All the testing has
+been done on an AthlonXP CPU with a specific version of gcc
+ gcc version 3.3.3 20040412 (Red Hat Linux 3.3.3-7)
+Other combinations of CPU and compiler can give different speeds. If the
+compiler is not able to simplify the group and batch structures and
+stores everything in memory instead of registers, performance will be
+low.
+
+Absolutely use a good compiler!
+
+Note: the same code can be compiled in C or C++ mode. g++ gives a 3%
+speed increase compared to gcc (I suppose some stricter constraint on
+array and pointers in C++ mode gives the optimizer more freedom).
+
+
+TRICK NUMBER a: a lot of brain work
+-----------------------------------
+The code started as very slow but correct implementation and was then
+tweaked for months with a lot of experimentation and by adding all the
+good ideas one after another to achieve little steps toward the best
+speed possible, while continously testing that nothing had been broken.
+
+Many hours were spent on this code.
+
+Enjoy the result.
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ * 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef FFTABLE_H
+#define FFTABLE_H
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data)
+{
+#if 0
+ *(((int *)tab)+2*g)=*((int *)data);
+ *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+#else
+ *(((long long *)tab)+g)=*((long long *)data);
+#endif
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g)
+{
+#if 1
+ *((int *)data)=*(((int *)tab)+2*g);
+ *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+#else
+ *((long long *)data)=*(((long long *)tab)+g);
+#endif
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g)
+{
+ for(int j=0;j<n;j++) *(data+j)^=*(tab+8*g+j);
+}
+
+#undef XOREQ_BEST_BY
+static inline void XOREQ_BEST_BY(unsigned char *d, unsigned char *s)
+{
+ XOR_BEST_BY(d, d, s);
+}
+
+#endif //FFTABLE_H
--- /dev/null
+all: logic
+
+logic: logic.o
+ gcc -o logic logic.o
+
+logic.o: logic.c
+ gcc -O3 -march=athlon-xp -c logic.c
+
+clean:
+ rm logic *.o
--- /dev/null
+/* logic -- synthetize logic functions with 4 inputs
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+
+
+/* Can we use negated inputs? */
+#define noNEGATEDTOO
+
+
+#include <stdio.h>
+
+
+/*
+ * abcd
+ */
+
+#define BINARY(b15,b14,b13,b12,b11,b10,b9,b8,b7,b6,b5,b4,b3,b2,b1,b0) \
+ ((b15)<<15)|((b14)<<14)|((b13)<<13)|((b12)<<12)| \
+ ((b11)<<11)|((b10)<<10)|((b9) << 9)|((b8) << 8)| \
+ ((b7) << 7)|((b6) << 6)|((b5) << 5)|((b4) << 4)| \
+ ((b3) << 3)|((b2) << 2)|((b1) << 1)|((b0) << 0)
+
+struct fun{
+ int level;
+ int op_type;
+ int op1;
+ int op2;
+};
+
+struct fun db[65536];
+int n_fun;
+
+#define LEVEL_ALOT 1000000
+
+#define OP_FALSE 0
+#define OP_TRUE 1
+#define OP_SRC 2
+#define OP_AND 3
+#define OP_OR 4
+#define OP_XOR 5
+
+#define SRC_A 10
+#define SRC_B 20
+#define SRC_C 30
+#define SRC_D 40
+#define SRC_AN 11
+#define SRC_BN 21
+#define SRC_CN 31
+#define SRC_DN 41
+
+void dump_element_prefix(int);
+void dump_element_infix(int);
+
+int main(void){
+ int i,j;
+ int l,p1,p2;
+ int candidate;
+ int max_p2_lev;
+
+ for(i=0;i<65536;i++){
+ db[i].level=LEVEL_ALOT;
+ }
+ n_fun=0;
+
+ db[0].level=0;
+ db[0].op_type=OP_FALSE;
+ n_fun++;
+
+ db[65535].level=0;
+ db[65535].op_type=OP_TRUE;
+ n_fun++;
+
+ db[BINARY(0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1)].level=0;
+ db[BINARY(0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1)].op_type=OP_SRC;
+ db[BINARY(0,0,0,0, 0,0,0,0, 1,1,1,1, 1,1,1,1)].op1=SRC_A;
+ n_fun++;
+
+ db[BINARY(0,0,0,0, 1,1,1,1, 0,0,0,0, 1,1,1,1)].level=0;
+ db[BINARY(0,0,0,0, 1,1,1,1, 0,0,0,0, 1,1,1,1)].op_type=OP_SRC;
+ db[BINARY(0,0,0,0, 1,1,1,1, 0,0,0,0, 1,1,1,1)].op1=SRC_B;
+ n_fun++;
+
+ db[BINARY(0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1)].level=0;
+ db[BINARY(0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1)].op_type=OP_SRC;
+ db[BINARY(0,0,1,1, 0,0,1,1, 0,0,1,1, 0,0,1,1)].op1=SRC_C;
+ n_fun++;
+
+ db[BINARY(0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1)].level=0;
+ db[BINARY(0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1)].op_type=OP_SRC;
+ db[BINARY(0,1,0,1, 0,1,0,1, 0,1,0,1, 0,1,0,1)].op1=SRC_D;
+ n_fun++;
+#ifdef NEGATEDTOO
+ db[BINARY(1,1,1,1, 1,1,1,1, 0,0,0,0, 0,0,0,0)].level=0;
+ db[BINARY(1,1,1,1, 1,1,1,1, 0,0,0,0, 0,0,0,0)].op_type=OP_SRC;
+ db[BINARY(1,1,1,1, 1,1,1,1, 0,0,0,0, 0,0,0,0)].op1=SRC_AN;
+ n_fun++;
+
+ db[BINARY(1,1,1,1, 0,0,0,0, 1,1,1,1, 0,0,0,0)].level=0;
+ db[BINARY(1,1,1,1, 0,0,0,0, 1,1,1,1, 0,0,0,0)].op_type=OP_SRC;
+ db[BINARY(1,1,1,1, 0,0,0,0, 1,1,1,1, 0,0,0,0)].op1=SRC_BN;
+ n_fun++;
+
+ db[BINARY(1,1,0,0, 1,1,0,0, 1,1,0,0, 1,1,0,0)].level=0;
+ db[BINARY(1,1,0,0, 1,1,0,0, 1,1,0,0, 1,1,0,0)].op_type=OP_SRC;
+ db[BINARY(1,1,0,0, 1,1,0,0, 1,1,0,0, 1,1,0,0)].op1=SRC_CN;
+ n_fun++;
+
+ db[BINARY(1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0)].level=0;
+ db[BINARY(1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0)].op_type=OP_SRC;
+ db[BINARY(1,0,1,0, 1,0,1,0, 1,0,1,0, 1,0,1,0)].op1=SRC_DN;
+ n_fun++;
+#endif
+
+ for(l=0;l<100;l++){
+ printf("calculating level %i\n",l);
+ for(p1=1;p1<65536;p1++){
+ if(db[p1].level==LEVEL_ALOT) continue;
+ max_p2_lev=l-db[p1].level-1;
+ for(p2=p1+1;p2<65536;p2++){
+ if(db[p2].level>max_p2_lev) continue;
+
+ candidate=p1&p2;
+ if(db[candidate].level==LEVEL_ALOT){
+ //found new
+ db[candidate].level=db[p1].level+db[p2].level+1;
+ db[candidate].op_type=OP_AND;
+ db[candidate].op1=p1;
+ db[candidate].op2=p2;
+ n_fun++;
+ }
+
+ candidate=p1|p2;
+ if(db[candidate].level==LEVEL_ALOT){
+ //found new
+ db[candidate].level=db[p1].level+db[p2].level+1;
+ db[candidate].op_type=OP_OR;
+ db[candidate].op1=p1;
+ db[candidate].op2=p2;
+ n_fun++;
+ }
+
+ candidate=p1^p2;
+ if(db[candidate].level==LEVEL_ALOT){
+ //found new
+ db[candidate].level=db[p1].level+db[p2].level+1;
+ db[candidate].op_type=OP_XOR;
+ db[candidate].op1=p1;
+ db[candidate].op2=p2;
+ n_fun++;
+ }
+
+ }
+ }
+ printf("num fun=%i\n\n",n_fun);
+ fflush(stdout);
+ if(n_fun>=65536) break;
+ }
+
+
+ for(i=0;i<65536;i++){
+ if(db[i].level==LEVEL_ALOT) continue;
+
+ printf("PREFIX ");
+ for(j=15;j>=0;j--){
+ printf("%i",i&(1<<j)?1:0);
+ if(j%4==0) printf(" ");
+ if(j%8==0) printf(" ");
+ }
+ printf(" : lev %2i: ",db[i].level);
+ dump_element_prefix(i);
+ printf("\n");
+
+ printf("INFIX ");
+ for(j=15;j>=0;j--){
+ printf("%i",i&(1<<j)?1:0);
+ if(j%4==0) printf(" ");
+ if(j%8==0) printf(" ");
+ }
+ printf(" : lev %2i: ",db[i].level);
+ dump_element_infix(i);
+ printf("\n");
+ }
+
+ return 0;
+}
+
+void dump_element_prefix(int e){
+ if(db[e].level==LEVEL_ALOT){
+ printf("PANIC!\n");
+ return;
+ };
+ switch(db[e].op_type){
+ case OP_FALSE:
+ printf("0");
+ break;
+ case OP_TRUE:
+ printf("1");
+ break;
+ case OP_SRC:
+ switch(db[e].op1){
+ case SRC_A:
+ printf("a");
+ break;
+ case SRC_B:
+ printf("b");
+ break;
+ case SRC_C:
+ printf("c");
+ break;
+ case SRC_D:
+ printf("d");
+ break;
+ case SRC_AN:
+ printf("an");
+ break;
+ case SRC_BN:
+ printf("bn");
+ break;
+ case SRC_CN:
+ printf("cn");
+ break;
+ case SRC_DN:
+ printf("dn");
+ break;
+ }
+ break;
+ case OP_AND:
+ printf("FFAND(");
+ dump_element_prefix(db[e].op1);
+ printf(",");
+ dump_element_prefix(db[e].op2);
+ printf(")");
+ break;
+ case OP_OR:
+ printf("FFOR(");
+ dump_element_prefix(db[e].op1);
+ printf(",");
+ dump_element_prefix(db[e].op2);
+ printf(")");
+ break;
+ case OP_XOR:
+ printf("FFXOR(");
+ dump_element_prefix(db[e].op1);
+ printf(",");
+ dump_element_prefix(db[e].op2);
+ printf(")");
+ break;
+ }
+}
+
+void dump_element_infix(int e){
+ if(db[e].level==LEVEL_ALOT){
+ printf("PANIC!\n");
+ return;
+ };
+ switch(db[e].op_type){
+ case OP_FALSE:
+ printf("0");
+ break;
+ case OP_TRUE:
+ printf("1");
+ break;
+ case OP_SRC:
+ switch(db[e].op1){
+ case SRC_A:
+ printf("a");
+ break;
+ case SRC_B:
+ printf("b");
+ break;
+ case SRC_C:
+ printf("c");
+ break;
+ case SRC_D:
+ printf("d");
+ break;
+ case SRC_AN:
+ printf("an");
+ break;
+ case SRC_BN:
+ printf("bn");
+ break;
+ case SRC_CN:
+ printf("cn");
+ break;
+ case SRC_DN:
+ printf("dn");
+ break;
+ }
+ break;
+ case OP_AND:
+ printf("( ");
+ dump_element_infix(db[e].op1);
+ printf("&");
+ dump_element_infix(db[e].op2);
+ printf(" )");
+ break;
+ case OP_OR:
+ printf("( ");
+ dump_element_infix(db[e].op1);
+ printf("|");
+ dump_element_infix(db[e].op2);
+ printf(" )");
+ break;
+ case OP_XOR:
+ printf("( ");
+ dump_element_infix(db[e].op1);
+ printf("^");
+ dump_element_infix(db[e].op2);
+ printf(" )");
+ break;
+ }
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+struct group_t{
+ unsigned char s1,s2,s3,s4;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 32
+
+group static inline FF0(){
+ group res;
+ res.s1=0x0;
+ res.s2=0x0;
+ res.s3=0x0;
+ res.s4=0x0;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ res.s1=0xff;
+ res.s2=0xff;
+ res.s3=0xff;
+ res.s4=0xff;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ res.s3=a.s3&b.s3;
+ res.s4=a.s4&b.s4;
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ res.s3=a.s3|b.s3;
+ res.s4=a.s4|b.s4;
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ res.s3=a.s3^b.s3;
+ res.s4=a.s4^b.s4;
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ res.s1=~a.s1;
+ res.s2=~a.s2;
+ res.s3=~a.s3;
+ res.s4=~a.s4;
+ return res;
+}
+
+
+/* 64 rows of 32 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+g)=*((int *)data);
+ *(((int *)tab)+32+g)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+g);
+ *(((int *)data)+1)=*(((int *)tab)+32+g);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+4*(g+(j>=4?32-1:0))+j);
+ }
+}
+
+struct batch_t{
+ unsigned char s1,s2,s3,s4;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 4
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ res.s3=a.s3&b.s3;
+ res.s4=a.s4&b.s4;
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ res.s3=a.s3|b.s3;
+ res.s4=a.s4|b.s4;
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ res.s3=a.s3^b.s3;
+ res.s4=a.s4^b.s4;
+ return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ res.s1=0x29;
+ res.s2=0x29;
+ res.s3=0x29;
+ res.s4=0x29;
+ return res;
+}
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ res.s1=0x02;
+ res.s2=0x02;
+ res.s3=0x02;
+ res.s4=0x02;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ res.s1=0x04;
+ res.s2=0x04;
+ res.s3=0x04;
+ res.s4=0x04;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ res.s1=0x10;
+ res.s2=0x10;
+ res.s3=0x10;
+ res.s4=0x10;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ res.s1=0x40;
+ res.s2=0x40;
+ res.s3=0x40;
+ res.s4=0x40;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ res.s1=0x80;
+ res.s2=0x80;
+ res.s3=0x80;
+ res.s4=0x80;
+ return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ res.s1=a.s1<<n;
+ res.s2=a.s2<<n;
+ res.s3=a.s3<<n;
+ res.s4=a.s4<<n;
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ res.s1=a.s1>>n;
+ res.s2=a.s2>>n;
+ res.s3=a.s3>>n;
+ res.s4=a.s4>>n;
+ return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+struct group_t{
+ unsigned char s1[4];
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 32
+
+group static inline FF0(){
+ group res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=0x0;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=0xff;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=a.s1[i]&b.s1[i];
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=a.s1[i]|b.s1[i];
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=a.s1[i]^b.s1[i];
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=~a.s1[i];
+ return res;
+}
+
+
+/* 64 rows of 32 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+g)=*((int *)data);
+ *(((int *)tab)+32+g)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+g);
+ *(((int *)data)+1)=*(((int *)tab)+32+g);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+4*(g+(j>=4?32-1:0))+j);
+ }
+}
+
+struct batch_t{
+ unsigned char s1[4];
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 4
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=a.s1[i]&b.s1[i];
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=a.s1[i]|b.s1[i];
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=a.s1[i]^b.s1[i];
+ return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=0x29;
+ return res;
+}
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=0x02;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=0x04;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=0x10;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=0x40;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=0x80;
+ return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=a.s1[i]<<n;
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ int i;
+ for(i=0;i<4;i++) res.s1[i]=a.s1[i]>>n;
+ return res;
+}
+
+void static inline M_EMPTY(void){
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "parallel_std_def.h"
+
+typedef unsigned int group;
+#define GROUP_PARALLELISM 32
+#define FF0() 0x0
+#define FF1() 0xffffffff
+
+/* 64 rows of 32 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+g)=*((int *)data);
+ *(((int *)tab)+32+g)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+g);
+ *(((int *)data)+1)=*(((int *)tab)+32+g);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+4*(g+(j>=4?32-1:0))+j);
+ }
+}
+
+typedef unsigned int batch;
+#define BYTES_PER_BATCH 4
+#define B_FFN_ALL_29() 0x29292929
+#define B_FFN_ALL_02() 0x02020202
+#define B_FFN_ALL_04() 0x04040404
+#define B_FFN_ALL_10() 0x10101010
+#define B_FFN_ALL_40() 0x40404040
+#define B_FFN_ALL_80() 0x80808080
+
+#define M_EMPTY()
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+struct group_t{
+ unsigned int s1;
+ unsigned int s2;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 64
+
+group static inline FF0(){
+ group res;
+ res.s1=0x0;
+ res.s2=0x0;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ res.s1=0xffffffff;
+ res.s2=0xffffffff;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ res.s1=~a.s1;
+ res.s2=~a.s2;
+ return res;
+}
+
+
+/* 64 rows of 64 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+2*g)=*((int *)data);
+ *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+2*g);
+ *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+8*g+j);
+ }
+}
+
+struct batch_t{
+ unsigned int s1;
+ unsigned int s2;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 8
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ res.s1=0x29292929;
+ res.s2=0x29292929;
+ return res;
+}
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ res.s1=0x02020202;
+ res.s2=0x02020202;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ res.s1=0x04040404;
+ res.s2=0x04040404;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ res.s1=0x10101010;
+ res.s2=0x10101010;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ res.s1=0x40404040;
+ res.s2=0x40404040;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ res.s1=0x80808080;
+ res.s2=0x80808080;
+ return res;
+}
+
+
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ res.s1=a.s1<<n;
+ res.s2=a.s2<<n;
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ res.s1=a.s1>>n;
+ res.s2=a.s2>>n;
+ return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+struct group_t{
+ unsigned char s1,s2,s3,s4,s5,s6,s7,s8;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 64
+
+group static inline FF0(){
+ group res;
+ res.s1=0x0;
+ res.s2=0x0;
+ res.s3=0x0;
+ res.s4=0x0;
+ res.s5=0x0;
+ res.s6=0x0;
+ res.s7=0x0;
+ res.s8=0x0;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ res.s1=0xff;
+ res.s2=0xff;
+ res.s3=0xff;
+ res.s4=0xff;
+ res.s5=0xff;
+ res.s6=0xff;
+ res.s7=0xff;
+ res.s8=0xff;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ res.s3=a.s3&b.s3;
+ res.s4=a.s4&b.s4;
+ res.s5=a.s5&b.s5;
+ res.s6=a.s6&b.s6;
+ res.s7=a.s7&b.s7;
+ res.s8=a.s8&b.s8;
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ res.s3=a.s3|b.s3;
+ res.s4=a.s4|b.s4;
+ res.s5=a.s5|b.s5;
+ res.s6=a.s6|b.s6;
+ res.s7=a.s7|b.s7;
+ res.s8=a.s8|b.s8;
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ res.s3=a.s3^b.s3;
+ res.s4=a.s4^b.s4;
+ res.s5=a.s5^b.s5;
+ res.s6=a.s6^b.s6;
+ res.s7=a.s7^b.s7;
+ res.s8=a.s8^b.s8;
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ res.s1=~a.s1;
+ res.s2=~a.s2;
+ res.s3=~a.s3;
+ res.s4=~a.s4;
+ res.s5=~a.s5;
+ res.s6=~a.s6;
+ res.s7=~a.s7;
+ res.s8=~a.s8;
+ return res;
+}
+
+
+/* 64 rows of 64 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+2*g)=*((int *)data);
+ *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+2*g);
+ *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+8*g+j);
+ }
+}
+
+struct batch_t{
+ unsigned char s1,s2,s3,s4,s5,s6,s7,s8;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 8
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ res.s3=a.s3&b.s3;
+ res.s4=a.s4&b.s4;
+ res.s5=a.s5&b.s5;
+ res.s6=a.s6&b.s6;
+ res.s7=a.s7&b.s7;
+ res.s8=a.s8&b.s8;
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ res.s3=a.s3|b.s3;
+ res.s4=a.s4|b.s4;
+ res.s5=a.s5|b.s5;
+ res.s6=a.s6|b.s6;
+ res.s7=a.s7|b.s7;
+ res.s8=a.s8|b.s8;
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ res.s3=a.s3^b.s3;
+ res.s4=a.s4^b.s4;
+ res.s5=a.s5^b.s5;
+ res.s6=a.s6^b.s6;
+ res.s7=a.s7^b.s7;
+ res.s8=a.s8^b.s8;
+ return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ res.s1=0x29;
+ res.s2=0x29;
+ res.s3=0x29;
+ res.s4=0x29;
+ res.s5=0x29;
+ res.s6=0x29;
+ res.s7=0x29;
+ res.s8=0x29;
+ return res;
+}
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ res.s1=0x02;
+ res.s2=0x02;
+ res.s3=0x02;
+ res.s4=0x02;
+ res.s5=0x02;
+ res.s6=0x02;
+ res.s7=0x02;
+ res.s8=0x02;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ res.s1=0x04;
+ res.s2=0x04;
+ res.s3=0x04;
+ res.s4=0x04;
+ res.s5=0x04;
+ res.s6=0x04;
+ res.s7=0x04;
+ res.s8=0x04;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ res.s1=0x10;
+ res.s2=0x10;
+ res.s3=0x10;
+ res.s4=0x10;
+ res.s5=0x10;
+ res.s6=0x10;
+ res.s7=0x10;
+ res.s8=0x10;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ res.s1=0x40;
+ res.s2=0x40;
+ res.s3=0x40;
+ res.s4=0x40;
+ res.s5=0x40;
+ res.s6=0x40;
+ res.s7=0x40;
+ res.s8=0x40;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ res.s1=0x80;
+ res.s2=0x80;
+ res.s3=0x80;
+ res.s4=0x80;
+ res.s5=0x80;
+ res.s6=0x80;
+ res.s7=0x80;
+ res.s8=0x80;
+ return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ res.s1=a.s1<<n;
+ res.s2=a.s2<<n;
+ res.s3=a.s3<<n;
+ res.s4=a.s4<<n;
+ res.s5=a.s5<<n;
+ res.s6=a.s6<<n;
+ res.s7=a.s7<<n;
+ res.s8=a.s8<<n;
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ res.s1=a.s1>>n;
+ res.s2=a.s2>>n;
+ res.s3=a.s3>>n;
+ res.s4=a.s4>>n;
+ res.s5=a.s5>>n;
+ res.s6=a.s6>>n;
+ res.s7=a.s7>>n;
+ res.s8=a.s8>>n;
+ return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+struct group_t{
+ unsigned char s1[8];
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 64
+
+group static inline FF0(){
+ group res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=0x0;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=0xff;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=a.s1[i]&b.s1[i];
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=a.s1[i]|b.s1[i];
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=a.s1[i]^b.s1[i];
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=~a.s1[i];
+ return res;
+}
+
+
+/* 64 rows of 64 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+2*g)=*((int *)data);
+ *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+2*g);
+ *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+8*g+j);
+ }
+}
+
+struct batch_t{
+ unsigned char s1[8];
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 8
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=a.s1[i]&b.s1[i];
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=a.s1[i]|b.s1[i];
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=a.s1[i]^b.s1[i];
+ return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=0x29;
+ return res;
+}
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=0x02;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=0x04;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=0x10;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=0x40;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=0x80;
+ return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=a.s1[i]<<n;
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ int i;
+ for(i=0;i<8;i++) res.s1[i]=a.s1[i]>>n;
+ return res;
+}
+
+void static inline M_EMPTY(void){
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ * 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "parallel_std_def.h"
+
+typedef unsigned long long group;
+#define GROUP_PARALLELISM 64
+#define FF0() 0x0ULL
+#define FF1() 0xffffffffffffffffULL
+
+typedef unsigned long long batch;
+#define BYTES_PER_BATCH 8
+#define B_FFN_ALL_29() 0x2929292929292929ULL
+#define B_FFN_ALL_02() 0x0202020202020202ULL
+#define B_FFN_ALL_04() 0x0404040404040404ULL
+#define B_FFN_ALL_10() 0x1010101010101010ULL
+#define B_FFN_ALL_40() 0x4040404040404040ULL
+#define B_FFN_ALL_80() 0x8080808080808080ULL
+
+#define M_EMPTY()
+
+#include "fftable.h"
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ * 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <mmintrin.h>
+
+#define MEMALIGN __attribute__((aligned(16)))
+
+union __u64 {
+ unsigned int u[2];
+ __m64 v;
+};
+
+static const union __u64 ff0 = {{0x00000000U, 0x00000000U}};
+static const union __u64 ff1 = {{0xffffffffU, 0xffffffffU}};
+
+typedef __m64 group;
+#define GROUP_PARALLELISM 64
+#define FF0() ff0.v
+#define FF1() ff1.v
+#define FFAND(a,b) _mm_and_si64((a),(b))
+#define FFOR(a,b) _mm_or_si64((a),(b))
+#define FFXOR(a,b) _mm_xor_si64((a),(b))
+#define FFNOT(a) _mm_xor_si64((a),FF1())
+
+/* 64 rows of 64 bits */
+
+static const union __u64 ff29 = {{0x29292929U, 0x29292929U}};
+static const union __u64 ff02 = {{0x02020202U, 0x02020202U}};
+static const union __u64 ff04 = {{0x04040404U, 0x04040404U}};
+static const union __u64 ff10 = {{0x10101010U, 0x10101010U}};
+static const union __u64 ff40 = {{0x40404040U, 0x40404040U}};
+static const union __u64 ff80 = {{0x80808080U, 0x80808080U}};
+
+typedef __m64 batch;
+#define BYTES_PER_BATCH 8
+#define B_FFAND(a,b) FFAND((a),(b))
+#define B_FFOR(a,b) FFOR((a),(b))
+#define B_FFXOR(a,b) FFXOR((a),(b))
+#define B_FFN_ALL_29() ff29.v
+#define B_FFN_ALL_02() ff02.v
+#define B_FFN_ALL_04() ff04.v
+#define B_FFN_ALL_10() ff10.v
+#define B_FFN_ALL_40() ff40.v
+#define B_FFN_ALL_80() ff80.v
+#define B_FFSH8L(a,n) _mm_slli_si64((a),(n))
+#define B_FFSH8R(a,n) _mm_srli_si64((a),(n))
+
+#define M_EMPTY() _mm_empty()
+
+
+#undef XOR_8_BY
+#define XOR_8_BY(d,s1,s2) do { *(__m64*)d = _mm_xor_si64(*(__m64*)(s1), *(__m64*)(s2)); } while(0)
+
+#undef XOREQ_8_BY
+#define XOREQ_8_BY(d,s) XOR_8_BY(d, d, s)
+
+#undef COPY_8_BY
+#define COPY_8_BY(d,s) do { *(__m64 *)(d) = *(__m64 *)(s); } while(0)
+
+#undef BEST_SPAN
+#define BEST_SPAN 8
+
+#undef XOR_BEST_BY
+#define XOR_BEST_BY(d,s1,s2) XOR_8_BY(d,s1,s2)
+
+#include "fftable.h"
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+struct group_t{
+ unsigned char s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+ group res;
+ res.s1=0x0;
+ res.s2=0x0;
+ res.s3=0x0;
+ res.s4=0x0;
+ res.s5=0x0;
+ res.s6=0x0;
+ res.s7=0x0;
+ res.s8=0x0;
+ res.s9=0x0;
+ res.s10=0x0;
+ res.s11=0x0;
+ res.s12=0x0;
+ res.s13=0x0;
+ res.s14=0x0;
+ res.s15=0x0;
+ res.s16=0x0;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ res.s1=0xff;
+ res.s2=0xff;
+ res.s3=0xff;
+ res.s4=0xff;
+ res.s5=0xff;
+ res.s6=0xff;
+ res.s7=0xff;
+ res.s8=0xff;
+ res.s9=0xff;
+ res.s10=0xff;
+ res.s11=0xff;
+ res.s12=0xff;
+ res.s13=0xff;
+ res.s14=0xff;
+ res.s15=0xff;
+ res.s16=0xff;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ res.s3=a.s3&b.s3;
+ res.s4=a.s4&b.s4;
+ res.s5=a.s5&b.s5;
+ res.s6=a.s6&b.s6;
+ res.s7=a.s7&b.s7;
+ res.s8=a.s8&b.s8;
+ res.s9=a.s9&b.s9;
+ res.s10=a.s10&b.s10;
+ res.s11=a.s11&b.s11;
+ res.s12=a.s12&b.s12;
+ res.s13=a.s13&b.s13;
+ res.s14=a.s14&b.s14;
+ res.s15=a.s15&b.s15;
+ res.s16=a.s16&b.s16;
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ res.s3=a.s3|b.s3;
+ res.s4=a.s4|b.s4;
+ res.s5=a.s5|b.s5;
+ res.s6=a.s6|b.s6;
+ res.s7=a.s7|b.s7;
+ res.s8=a.s8|b.s8;
+ res.s9=a.s9|b.s9;
+ res.s10=a.s10|b.s10;
+ res.s11=a.s11|b.s11;
+ res.s12=a.s12|b.s12;
+ res.s13=a.s13|b.s13;
+ res.s14=a.s14|b.s14;
+ res.s15=a.s15|b.s15;
+ res.s16=a.s16|b.s16;
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ res.s3=a.s3^b.s3;
+ res.s4=a.s4^b.s4;
+ res.s5=a.s5^b.s5;
+ res.s6=a.s6^b.s6;
+ res.s7=a.s7^b.s7;
+ res.s8=a.s8^b.s8;
+ res.s9=a.s9^b.s9;
+ res.s10=a.s10^b.s10;
+ res.s11=a.s11^b.s11;
+ res.s12=a.s12^b.s12;
+ res.s13=a.s13^b.s13;
+ res.s14=a.s14^b.s14;
+ res.s15=a.s15^b.s15;
+ res.s16=a.s16^b.s16;
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ res.s1=~a.s1;
+ res.s2=~a.s2;
+ res.s3=~a.s3;
+ res.s4=~a.s4;
+ res.s5=~a.s5;
+ res.s6=~a.s6;
+ res.s7=~a.s7;
+ res.s8=~a.s8;
+ res.s9=~a.s9;
+ res.s10=~a.s10;
+ res.s11=~a.s11;
+ res.s12=~a.s12;
+ res.s13=~a.s13;
+ res.s14=~a.s14;
+ res.s15=~a.s15;
+ res.s16=~a.s16;
+ return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+2*g)=*((int *)data);
+ *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+2*g);
+ *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+8*g+j);
+ }
+}
+
+
+struct batch_t{
+ unsigned char s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12,s13,s14,s15,s16;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ res.s3=a.s3&b.s3;
+ res.s4=a.s4&b.s4;
+ res.s5=a.s5&b.s5;
+ res.s6=a.s6&b.s6;
+ res.s7=a.s7&b.s7;
+ res.s8=a.s8&b.s8;
+ res.s9=a.s9&b.s9;
+ res.s10=a.s10&b.s10;
+ res.s11=a.s11&b.s11;
+ res.s12=a.s12&b.s12;
+ res.s13=a.s13&b.s13;
+ res.s14=a.s14&b.s14;
+ res.s15=a.s15&b.s15;
+ res.s16=a.s16&b.s16;
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ res.s3=a.s3|b.s3;
+ res.s4=a.s4|b.s4;
+ res.s5=a.s5|b.s5;
+ res.s6=a.s6|b.s6;
+ res.s7=a.s7|b.s7;
+ res.s8=a.s8|b.s8;
+ res.s9=a.s9|b.s9;
+ res.s10=a.s10|b.s10;
+ res.s11=a.s11|b.s11;
+ res.s12=a.s12|b.s12;
+ res.s13=a.s13|b.s13;
+ res.s14=a.s14|b.s14;
+ res.s15=a.s15|b.s15;
+ res.s16=a.s16|b.s16;
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ res.s3=a.s3^b.s3;
+ res.s4=a.s4^b.s4;
+ res.s5=a.s5^b.s5;
+ res.s6=a.s6^b.s6;
+ res.s7=a.s7^b.s7;
+ res.s8=a.s8^b.s8;
+ res.s9=a.s9^b.s9;
+ res.s10=a.s10^b.s10;
+ res.s11=a.s11^b.s11;
+ res.s12=a.s12^b.s12;
+ res.s13=a.s13^b.s13;
+ res.s14=a.s14^b.s14;
+ res.s15=a.s15^b.s15;
+ res.s16=a.s16^b.s16;
+ return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ res.s1=0x29;
+ res.s2=0x29;
+ res.s3=0x29;
+ res.s4=0x29;
+ res.s5=0x29;
+ res.s6=0x29;
+ res.s7=0x29;
+ res.s8=0x29;
+ res.s9=0x29;
+ res.s10=0x29;
+ res.s11=0x29;
+ res.s12=0x29;
+ res.s13=0x29;
+ res.s14=0x29;
+ res.s15=0x29;
+ res.s16=0x29;
+ return res;
+}
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ res.s1=0x02;
+ res.s2=0x02;
+ res.s3=0x02;
+ res.s4=0x02;
+ res.s5=0x02;
+ res.s6=0x02;
+ res.s7=0x02;
+ res.s8=0x02;
+ res.s9=0x02;
+ res.s10=0x02;
+ res.s11=0x02;
+ res.s12=0x02;
+ res.s13=0x02;
+ res.s14=0x02;
+ res.s15=0x02;
+ res.s16=0x02;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ res.s1=0x04;
+ res.s2=0x04;
+ res.s3=0x04;
+ res.s4=0x04;
+ res.s5=0x04;
+ res.s6=0x04;
+ res.s7=0x04;
+ res.s8=0x04;
+ res.s9=0x04;
+ res.s10=0x04;
+ res.s11=0x04;
+ res.s12=0x04;
+ res.s13=0x04;
+ res.s14=0x04;
+ res.s15=0x04;
+ res.s16=0x04;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ res.s1=0x10;
+ res.s2=0x10;
+ res.s3=0x10;
+ res.s4=0x10;
+ res.s5=0x10;
+ res.s6=0x10;
+ res.s7=0x10;
+ res.s8=0x10;
+ res.s9=0x10;
+ res.s10=0x10;
+ res.s11=0x10;
+ res.s12=0x10;
+ res.s13=0x10;
+ res.s14=0x10;
+ res.s15=0x10;
+ res.s16=0x10;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ res.s1=0x40;
+ res.s2=0x40;
+ res.s3=0x40;
+ res.s4=0x40;
+ res.s5=0x40;
+ res.s6=0x40;
+ res.s7=0x40;
+ res.s8=0x40;
+ res.s9=0x40;
+ res.s10=0x40;
+ res.s11=0x40;
+ res.s12=0x40;
+ res.s13=0x40;
+ res.s14=0x40;
+ res.s15=0x40;
+ res.s16=0x40;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ res.s1=0x80;
+ res.s2=0x80;
+ res.s3=0x80;
+ res.s4=0x80;
+ res.s5=0x80;
+ res.s6=0x80;
+ res.s7=0x80;
+ res.s8=0x80;
+ res.s9=0x80;
+ res.s10=0x80;
+ res.s11=0x80;
+ res.s12=0x80;
+ res.s13=0x80;
+ res.s14=0x80;
+ res.s15=0x80;
+ res.s16=0x80;
+ return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ res.s1=a.s1<<n;
+ res.s2=a.s2<<n;
+ res.s3=a.s3<<n;
+ res.s4=a.s4<<n;
+ res.s5=a.s5<<n;
+ res.s6=a.s6<<n;
+ res.s7=a.s7<<n;
+ res.s8=a.s8<<n;
+ res.s9=a.s9<<n;
+ res.s10=a.s10<<n;
+ res.s11=a.s11<<n;
+ res.s12=a.s12<<n;
+ res.s13=a.s13<<n;
+ res.s14=a.s14<<n;
+ res.s15=a.s15<<n;
+ res.s16=a.s16<<n;
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ res.s1=a.s1>>n;
+ res.s2=a.s2>>n;
+ res.s3=a.s3>>n;
+ res.s4=a.s4>>n;
+ res.s5=a.s5>>n;
+ res.s6=a.s6>>n;
+ res.s7=a.s7>>n;
+ res.s8=a.s8>>n;
+ res.s9=a.s9>>n;
+ res.s10=a.s10>>n;
+ res.s11=a.s11>>n;
+ res.s12=a.s12>>n;
+ res.s13=a.s13>>n;
+ res.s14=a.s14>>n;
+ res.s15=a.s15>>n;
+ res.s16=a.s16>>n;
+ return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+struct group_t{
+ unsigned char s1[16];
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+ group res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=0x0;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=0xff;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=a.s1[i]&b.s1[i];
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=a.s1[i]|b.s1[i];
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=a.s1[i]^b.s1[i];
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=~a.s1[i];
+ return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+2*g)=*((int *)data);
+ *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+2*g);
+ *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+8*g+j);
+ }
+}
+
+
+struct batch_t{
+ unsigned char s1[16];
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=a.s1[i]&b.s1[i];
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=a.s1[i]|b.s1[i];
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=a.s1[i]^b.s1[i];
+ return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=0x29;
+ return res;
+}
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=0x02;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=0x04;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=0x10;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=0x40;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=0x80;
+ return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=a.s1[i]<<n;
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ int i;
+ for(i=0;i<16;i++) res.s1[i]=a.s1[i]>>n;
+ return res;
+}
+
+void static inline M_EMPTY(void){
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+struct group_t{
+ unsigned long long int s1;
+ unsigned long long int s2;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+ group res;
+ res.s1=0x0ULL;
+ res.s2=0x0ULL;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ res.s1=0xffffffffffffffffULL;
+ res.s2=0xffffffffffffffffULL;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ res.s1=~a.s1;
+ res.s2=~a.s2;
+ return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+2*g)=*((int *)data);
+ *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+2*g);
+ *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+8*g+j);
+ }
+}
+
+
+struct batch_t{
+ unsigned long long int s1;
+ unsigned long long int s2;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ res.s1=0x2929292929292929ULL;
+ res.s2=0x2929292929292929ULL;
+ return res;
+}
+
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ res.s1=0x0202020202020202ULL;
+ res.s2=0x0202020202020202ULL;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ res.s1=0x0404040404040404ULL;
+ res.s2=0x0404040404040404ULL;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ res.s1=0x1010101010101010ULL;
+ res.s2=0x1010101010101010ULL;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ res.s1=0x4040404040404040ULL;
+ res.s2=0x4040404040404040ULL;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ res.s1=0x8080808080808080ULL;
+ res.s2=0x8080808080808080ULL;
+ return res;
+}
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ res.s1=a.s1<<n;
+ res.s2=a.s2<<n;
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ res.s1=a.s1>>n;
+ res.s2=a.s2>>n;
+ return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <mmintrin.h>
+
+#define MEMALIGN __attribute__((aligned(16)))
+
+struct group_t{
+ __m64 s1,s2;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+ group res;
+ res.s1=(__m64)0x0ULL;
+ res.s2=(__m64)0x0ULL;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ res.s1=(__m64)0xffffffffffffffffULL;
+ res.s2=(__m64)0xffffffffffffffffULL;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ res.s1=_m_pand(a.s1,b.s1);
+ res.s2=_m_pand(a.s2,b.s2);
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ res.s1=_m_por(a.s1,b.s1);
+ res.s2=_m_por(a.s2,b.s2);
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ res.s1=_m_pxor(a.s1,b.s1);
+ res.s2=_m_pxor(a.s2,b.s2);
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ res.s1=_m_pxor(a.s1,FF1().s1);
+ res.s2=_m_pxor(a.s2,FF1().s2);
+ return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+2*g)=*((int *)data);
+ *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+2*g);
+ *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+8*g+j);
+ }
+}
+
+
+struct batch_t{
+ __m64 s1,s2;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ res.s1=_m_pand(a.s1,b.s1);
+ res.s2=_m_pand(a.s2,b.s2);
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ res.s1=_m_por(a.s1,b.s1);
+ res.s2=_m_por(a.s2,b.s2);
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ res.s1=_m_pxor(a.s1,b.s1);
+ res.s2=_m_pxor(a.s2,b.s2);
+ return res;
+}
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ res.s1=(__m64)0x2929292929292929ULL;
+ res.s2=(__m64)0x2929292929292929ULL;
+ return res;
+}
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ res.s1=(__m64)0x0202020202020202ULL;
+ res.s2=(__m64)0x0202020202020202ULL;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ res.s1=(__m64)0x0404040404040404ULL;
+ res.s2=(__m64)0x0404040404040404ULL;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ res.s1=(__m64)0x1010101010101010ULL;
+ res.s2=(__m64)0x1010101010101010ULL;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ res.s1=(__m64)0x4040404040404040ULL;
+ res.s2=(__m64)0x4040404040404040ULL;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ res.s1=(__m64)0x8080808080808080ULL;
+ res.s2=(__m64)0x8080808080808080ULL;
+ return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ res.s1=_m_psllqi(a.s1,n);
+ res.s2=_m_psllqi(a.s2,n);
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ res.s1=_m_psrlqi(a.s1,n);
+ res.s2=_m_psrlqi(a.s2,n);
+ return res;
+}
+
+void static inline M_EMPTY(void){
+ _m_empty();
+}
+
+
+#undef XOR_8_BY
+#define XOR_8_BY(d,s1,s2) do{ __m64 *pd=(__m64 *)(d), *ps1=(__m64 *)(s1), *ps2=(__m64 *)(s2); \
+ *pd = _m_pxor( *ps1 , *ps2 ); }while(0)
+
+#undef XOREQ_8_BY
+#define XOREQ_8_BY(d,s) do{ __m64 *pd=(__m64 *)(d), *ps=(__m64 *)(s); \
+ *pd = _m_pxor( *ps, *pd ); }while(0)
+
+#undef COPY_8_BY
+#define COPY_8_BY(d,s) do{ __m64 *pd=(__m64 *)(d), *ps=(__m64 *)(s); \
+ *pd = *ps; }while(0)
+
+#undef BEST_SPAN
+#define BEST_SPAN 8
+
+#undef XOR_BEST_BY
+#define XOR_BEST_BY(d,s1,s2) do{ XOR_8_BY(d,s1,s2); }while(0);
+
+#undef XOREQ_BEST_BY
+#define XOREQ_BEST_BY(d,s) do{ XOREQ_8_BY(d,s); }while(0);
+
+#undef COPY_BEST_BY
+#define COPY_BEST_BY(d,s) do{ COPY_8_BY(d,s); }while(0);
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+struct group_t{
+ unsigned int s1,s2,s3,s4;
+};
+typedef struct group_t group;
+
+#define GROUP_PARALLELISM 128
+
+group static inline FF0(){
+ group res;
+ res.s1=0x0;
+ res.s2=0x0;
+ res.s3=0x0;
+ res.s4=0x0;
+ return res;
+}
+
+group static inline FF1(){
+ group res;
+ res.s1=0xffffffff;
+ res.s2=0xffffffff;
+ res.s3=0xffffffff;
+ res.s4=0xffffffff;
+ return res;
+}
+
+group static inline FFAND(group a,group b){
+ group res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ res.s3=a.s3&b.s3;
+ res.s4=a.s4&b.s4;
+ return res;
+}
+
+group static inline FFOR(group a,group b){
+ group res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ res.s3=a.s3|b.s3;
+ res.s4=a.s4|b.s4;
+ return res;
+}
+
+group static inline FFXOR(group a,group b){
+ group res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ res.s3=a.s3^b.s3;
+ res.s4=a.s4^b.s4;
+ return res;
+}
+
+group static inline FFNOT(group a){
+ group res;
+ res.s1=~a.s1;
+ res.s2=~a.s2;
+ res.s3=~a.s3;
+ res.s4=~a.s4;
+ return res;
+}
+
+
+/* 64 rows of 128 bits */
+
+void static inline FFTABLEIN(unsigned char *tab, int g, unsigned char *data){
+ *(((int *)tab)+2*g)=*((int *)data);
+ *(((int *)tab)+2*g+1)=*(((int *)data)+1);
+}
+
+void static inline FFTABLEOUT(unsigned char *data, unsigned char *tab, int g){
+ *((int *)data)=*(((int *)tab)+2*g);
+ *(((int *)data)+1)=*(((int *)tab)+2*g+1);
+}
+
+void static inline FFTABLEOUTXORNBY(int n, unsigned char *data, unsigned char *tab, int g){
+ int j;
+ for(j=0;j<n;j++){
+ *(data+j)^=*(tab+8*g+j);
+ }
+}
+
+
+struct batch_t{
+ unsigned int s1,s2,s3,s4;
+};
+typedef struct batch_t batch;
+
+#define BYTES_PER_BATCH 16
+
+batch static inline B_FFAND(batch a,batch b){
+ batch res;
+ res.s1=a.s1&b.s1;
+ res.s2=a.s2&b.s2;
+ res.s3=a.s3&b.s3;
+ res.s4=a.s4&b.s4;
+ return res;
+}
+
+batch static inline B_FFOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1|b.s1;
+ res.s2=a.s2|b.s2;
+ res.s3=a.s3|b.s3;
+ res.s4=a.s4|b.s4;
+ return res;
+}
+
+batch static inline B_FFXOR(batch a,batch b){
+ batch res;
+ res.s1=a.s1^b.s1;
+ res.s2=a.s2^b.s2;
+ res.s3=a.s3^b.s3;
+ res.s4=a.s4^b.s4;
+ return res;
+}
+
+
+batch static inline B_FFN_ALL_29(){
+ batch res;
+ res.s1=0x29292929;
+ res.s2=0x29292929;
+ res.s3=0x29292929;
+ res.s4=0x29292929;
+ return res;
+}
+batch static inline B_FFN_ALL_02(){
+ batch res;
+ res.s1=0x02020202;
+ res.s2=0x02020202;
+ res.s3=0x02020202;
+ res.s4=0x02020202;
+ return res;
+}
+batch static inline B_FFN_ALL_04(){
+ batch res;
+ res.s1=0x04040404;
+ res.s2=0x04040404;
+ res.s3=0x04040404;
+ res.s4=0x04040404;
+ return res;
+}
+batch static inline B_FFN_ALL_10(){
+ batch res;
+ res.s1=0x10101010;
+ res.s2=0x10101010;
+ res.s3=0x10101010;
+ res.s4=0x10101010;
+ return res;
+}
+batch static inline B_FFN_ALL_40(){
+ batch res;
+ res.s1=0x40404040;
+ res.s2=0x40404040;
+ res.s3=0x40404040;
+ res.s4=0x40404040;
+ return res;
+}
+batch static inline B_FFN_ALL_80(){
+ batch res;
+ res.s1=0x80808080;
+ res.s2=0x80808080;
+ res.s3=0x80808080;
+ res.s4=0x80808080;
+ return res;
+}
+
+batch static inline B_FFSH8L(batch a,int n){
+ batch res;
+ res.s1=a.s1<<n;
+ res.s2=a.s2<<n;
+ res.s3=a.s3<<n;
+ res.s4=a.s4<<n;
+ return res;
+}
+
+batch static inline B_FFSH8R(batch a,int n){
+ batch res;
+ res.s1=a.s1>>n;
+ res.s2=a.s2>>n;
+ res.s3=a.s3>>n;
+ res.s4=a.s4>>n;
+ return res;
+}
+
+
+void static inline M_EMPTY(void){
+}
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ * 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <xmmintrin.h>
+
+#define MEMALIGN __attribute__((aligned(16)))
+
+union __u128 {
+ unsigned int u[4];
+ __m128 v;
+};
+
+static const union __u128 ff0 = {{0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}};
+static const union __u128 ff1 = {{0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU}};
+
+typedef __m128 group;
+#define GROUP_PARALLELISM 128
+#define FF0() ff0.v
+#define FF1() ff1.v
+#define FFAND(a,b) _mm_and_ps((a),(b))
+#define FFOR(a,b) _mm_or_ps((a),(b))
+#define FFXOR(a,b) _mm_xor_ps((a),(b))
+#define FFNOT(a) _mm_xor_ps((a),FF1())
+#define MALLOC(X) _mm_malloc(X,16)
+#define FREE(X) _mm_free(X)
+
+union __u64 {
+ unsigned int u[2];
+ __m64 v;
+};
+
+static const union __u64 ff29 = {{0x29292929U, 0x29292929U}};
+static const union __u64 ff02 = {{0x02020202U, 0x02020202U}};
+static const union __u64 ff04 = {{0x04040404U, 0x04040404U}};
+static const union __u64 ff10 = {{0x10101010U, 0x10101010U}};
+static const union __u64 ff40 = {{0x40404040U, 0x40404040U}};
+static const union __u64 ff80 = {{0x80808080U, 0x80808080U}};
+
+typedef __m64 batch;
+#define BYTES_PER_BATCH 8
+#define B_FFN_ALL_29() ff29.v
+#define B_FFN_ALL_02() ff02.v
+#define B_FFN_ALL_04() ff04.v
+#define B_FFN_ALL_10() ff10.v
+#define B_FFN_ALL_40() ff40.v
+#define B_FFN_ALL_80() ff80.v
+#define B_FFAND(a,b) _mm_and_si64((a),(b))
+#define B_FFOR(a,b) _mm_or_si64((a),(b))
+#define B_FFXOR(a,b) _mm_xor_si64((a),(b))
+#define B_FFSH8L(a,n) _mm_slli_si64((a),(n))
+#define B_FFSH8R(a,n) _mm_srli_si64((a),(n))
+
+#define M_EMPTY() _mm_empty()
+
+
+#undef XOR_8_BY
+#define XOR_8_BY(d,s1,s2) do { *(__m64*)d = _mm_xor_si64(*(__m64*)(s1), *(__m64*)(s2)); } while(0)
+
+#undef XOREQ_8_BY
+#define XOREQ_8_BY(d,s) XOR_8_BY(d, d, s)
+
+#undef COPY_8_BY
+#define COPY_8_BY(d,s) do { *(__m64 *)(d) = *(__m64 *)(s); } while(0)
+
+#undef BEST_SPAN
+#define BEST_SPAN 16
+
+#undef XOR_BEST_BY
+static inline void XOR_BEST_BY(unsigned char *d, unsigned char *s1, unsigned char *s2)
+{
+ __m128 vs1 = _mm_load_ps((float*)s1);
+ __m128 vs2 = _mm_load_ps((float*)s2);
+ vs1 = _mm_xor_ps(vs1, vs2);
+ _mm_store_ps((float*)d, vs1);
+}
+
+#include "fftable.h"
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2007 Dark Avenger
+ * 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <emmintrin.h>
+
+#define MEMALIGN __attribute__((aligned(16)))
+
+union __u128i {
+ unsigned int u[4];
+ __m128i v;
+};
+
+static const union __u128i ff0 = {{0x00000000U, 0x00000000U, 0x00000000U, 0x00000000U}};
+static const union __u128i ff1 = {{0xffffffffU, 0xffffffffU, 0xffffffffU, 0xffffffffU}};
+
+typedef __m128i group;
+#define GROUP_PARALLELISM 128
+#define FF0() ff0.v
+#define FF1() ff1.v
+#define FFAND(a,b) _mm_and_si128((a),(b))
+#define FFOR(a,b) _mm_or_si128((a),(b))
+#define FFXOR(a,b) _mm_xor_si128((a),(b))
+#define FFNOT(a) _mm_xor_si128((a),FF1())
+#define MALLOC(X) _mm_malloc(X,16)
+#define FREE(X) _mm_free(X)
+
+/* BATCH */
+
+static const union __u128i ff29 = {{0x29292929U, 0x29292929U, 0x29292929U, 0x29292929U}};
+static const union __u128i ff02 = {{0x02020202U, 0x02020202U, 0x02020202U, 0x02020202U}};
+static const union __u128i ff04 = {{0x04040404U, 0x04040404U, 0x04040404U, 0x04040404U}};
+static const union __u128i ff10 = {{0x10101010U, 0x10101010U, 0x10101010U, 0x10101010U}};
+static const union __u128i ff40 = {{0x40404040U, 0x40404040U, 0x40404040U, 0x40404040U}};
+static const union __u128i ff80 = {{0x80808080U, 0x80808080U, 0x80808080U, 0x80808080U}};
+
+typedef __m128i batch;
+#define BYTES_PER_BATCH 16
+#define B_FFN_ALL_29() ff29.v
+#define B_FFN_ALL_02() ff02.v
+#define B_FFN_ALL_04() ff04.v
+#define B_FFN_ALL_10() ff10.v
+#define B_FFN_ALL_40() ff40.v
+#define B_FFN_ALL_80() ff80.v
+
+#define B_FFAND(a,b) FFAND(a,b)
+#define B_FFOR(a,b) FFOR(a,b)
+#define B_FFXOR(a,b) FFXOR(a,b)
+#define B_FFSH8L(a,n) _mm_slli_epi64((a),(n))
+#define B_FFSH8R(a,n) _mm_srli_epi64((a),(n))
+
+#define M_EMPTY()
+
+#undef BEST_SPAN
+#define BEST_SPAN 16
+
+#undef XOR_BEST_BY
+static inline void XOR_BEST_BY(unsigned char *d, unsigned char *s1, unsigned char *s2)
+{
+ __m128i vs1 = _mm_load_si128((__m128i*)s1);
+ __m128i vs2 = _mm_load_si128((__m128i*)s2);
+ vs1 = _mm_xor_si128(vs1, vs2);
+ _mm_store_si128((__m128i*)d, vs1);
+}
+
+#include "fftable.h"
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+
+#if 0
+//// generics
+#define COPY4BY(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \
+ *pd = *ps; }while(0)
+#define COPY8BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+ *pd = *ps; }while(0)
+#define COPY16BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+ *pd = *ps; \
+ *(pd+1) = *(ps+1); }while(0)
+#define COPY32BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+ *pd = *ps; \
+ *(pd+1) = *(ps+1) \
+ *(pd+2) = *(ps+2) \
+ *(pd+3) = *(ps+3); }while(0)
+#define XOR4BY(d,s1,s2) do{ int *pd=(int *)(d), *ps1=(int *)(s1), *ps2=(int *)(s2); \
+ *pd = *ps1 ^ *ps2; }while(0)
+#define XOR8BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \
+ *pd = *ps1 ^ *ps2; }while(0)
+#define XOR16BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \
+ *pd = *ps1 ^ *ps2; \
+ *(pd+8) = *(ps1+8) ^ *(ps2+8); }while(0)
+#define XOR32BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \
+ *pd = *ps1 ^ *ps2; \
+ *(pd+1) = *(ps1+1) ^ *(ps2+1); \
+ *(pd+2) = *(ps1+2) ^ *(ps2+2); \
+ *(pd+3) = *(ps1+3) ^ *(ps2+3); }while(0)
+#define XOR32BV(d,s1,s2) do{ int *const pd=(int *const)(d), *ps1=(const int *const)(s1), *ps2=(const int *const)(s2); \
+ int z; \
+ for(z=0;z<8;z++){ \
+ pd[z]=ps1[z]^ps2[z]; \
+ } \
+ }while(0)
+#define XOREQ4BY(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \
+ *pd ^= *ps; }while(0)
+#define XOREQ8BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+ *pd ^= *ps; }while(0)
+#define XOREQ16BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+ *pd ^= *ps; \
+ *(pd+1) ^=*(ps+1); }while(0)
+#define XOREQ32BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+ *pd ^= *ps; \
+ *(pd+1) ^=*(ps+1); \
+ *(pd+2) ^=*(ps+2); \
+ *(pd+3) ^=*(ps+3); }while(0)
+#define XOREQ32BY4(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \
+ *pd ^= *ps; \
+ *(pd+1) ^=*(ps+1); \
+ *(pd+2) ^=*(ps+2); \
+ *(pd+3) ^=*(ps+3); \
+ *(pd+4) ^=*(ps+4); \
+ *(pd+5) ^=*(ps+5); \
+ *(pd+6) ^=*(ps+6); \
+ *(pd+7) ^=*(ps+7); }while(0)
+#define XOREQ32BV(d,s) do{ unsigned char *pd=(unsigned char *)(d), *ps=(unsigned char *)(s); \
+ int z; \
+ for(z=0;z<32;z++){ \
+ pd[z]^=ps[z]; \
+ } \
+ }while(0)
+
+#else
+#define XOR_4_BY(d,s1,s2) do{ int *pd=(int *)(d), *ps1=(int *)(s1), *ps2=(int *)(s2); \
+ *pd = *ps1 ^ *ps2; }while(0)
+#define XOR_8_BY(d,s1,s2) do{ long long int *pd=(long long int *)(d), *ps1=(long long int *)(s1), *ps2=(long long int *)(s2); \
+ *pd = *ps1 ^ *ps2; }while(0)
+#define XOREQ_4_BY(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \
+ *pd ^= *ps; }while(0)
+#define XOREQ_8_BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+ *pd ^= *ps; }while(0)
+#define COPY_4_BY(d,s) do{ int *pd=(int *)(d), *ps=(int *)(s); \
+ *pd = *ps; }while(0)
+#define COPY_8_BY(d,s) do{ long long int *pd=(long long int *)(d), *ps=(long long int *)(s); \
+ *pd = *ps; }while(0)
+
+#define BEST_SPAN 8
+#define XOR_BEST_BY(d,s1,s2) do{ XOR_8_BY(d,s1,s2); }while(0);
+#define XOREQ_BEST_BY(d,s) do{ XOREQ_8_BY(d,s); }while(0);
+#define COPY_BEST_BY(d,s) do{ COPY_8_BY(d,s); }while(0);
+
+#define END_MM do{ }while(0);
+#endif
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define FFXOR(a,b) ((a)^(b))
+#define FFAND(a,b) ((a)&(b))
+#define FFOR(a,b) ((a)|(b))
+#define FFNOT(a) (~(a))
+
+#define B_FFAND(a,b) ((a)&(b))
+#define B_FFOR(a,b) ((a)|(b))
+#define B_FFXOR(a,b) ((a)^(b))
+#define B_FFSH8L(a,n) ((a)<<(n))
+#define B_FFSH8R(a,n) ((a)>>(n))
--- /dev/null
+/* FFdecsa -- fast decsa algorithm
+ *
+ * Copyright (C) 2003-2004 fatih89r
+ *
+ * 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, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+
+// define statics only once, when STREAM_INIT
+#ifdef STREAM_INIT
+struct stream_regs {
+ group A[32+10][4]; // 32 because we will move back (virtual shift register)
+ group B[32+10][4]; // 32 because we will move back (virtual shift register)
+ group X[4];
+ group Y[4];
+ group Z[4];
+ group D[4];
+ group E[4];
+ group F[4];
+ group p;
+ group q;
+ group r;
+ };
+
+static inline void trasp64_32_88ccw(unsigned char *data){
+/* 64 rows of 32 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/
+#define row ((unsigned int *)data)
+ int i,j;
+ for(j=0;j<64;j+=32){
+ unsigned int t,b;
+ for(i=0;i<16;i++){
+ t=row[j+i];
+ b=row[j+16+i];
+ row[j+i] = (t&0x0000ffff) | ((b )<<16);
+ row[j+16+i]=((t )>>16) | (b&0xffff0000) ;
+ }
+ }
+ for(j=0;j<64;j+=16){
+ unsigned int t,b;
+ for(i=0;i<8;i++){
+ t=row[j+i];
+ b=row[j+8+i];
+ row[j+i] = (t&0x00ff00ff) | ((b&0x00ff00ff)<<8);
+ row[j+8+i] =((t&0xff00ff00)>>8) | (b&0xff00ff00);
+ }
+ }
+ for(j=0;j<64;j+=8){
+ unsigned int t,b;
+ for(i=0;i<4;i++){
+ t=row[j+i];
+ b=row[j+4+i];
+ row[j+i] =((t&0x0f0f0f0f)<<4) | (b&0x0f0f0f0f);
+ row[j+4+i] = (t&0xf0f0f0f0) | ((b&0xf0f0f0f0)>>4);
+ }
+ }
+ for(j=0;j<64;j+=4){
+ unsigned int t,b;
+ for(i=0;i<2;i++){
+ t=row[j+i];
+ b=row[j+2+i];
+ row[j+i] =((t&0x33333333)<<2) | (b&0x33333333);
+ row[j+2+i] = (t&0xcccccccc) | ((b&0xcccccccc)>>2);
+ }
+ }
+ for(j=0;j<64;j+=2){
+ unsigned int t,b;
+ for(i=0;i<1;i++){
+ t=row[j+i];
+ b=row[j+1+i];
+ row[j+i] =((t&0x55555555)<<1) | (b&0x55555555);
+ row[j+1+i] = (t&0xaaaaaaaa) | ((b&0xaaaaaaaa)>>1);
+ }
+ }
+#undef row
+}
+
+static inline void trasp64_32_88cw(unsigned char *data){
+/* 64 rows of 32 bits transposition (bytes transp. - 8x8 rotate clockwise)*/
+#define row ((unsigned int *)data)
+ int i,j;
+ for(j=0;j<64;j+=32){
+ unsigned int t,b;
+ for(i=0;i<16;i++){
+ t=row[j+i];
+ b=row[j+16+i];
+ row[j+i] = (t&0x0000ffff) | ((b )<<16);
+ row[j+16+i]=((t )>>16) | (b&0xffff0000) ;
+ }
+ }
+ for(j=0;j<64;j+=16){
+ unsigned int t,b;
+ for(i=0;i<8;i++){
+ t=row[j+i];
+ b=row[j+8+i];
+ row[j+i] = (t&0x00ff00ff) | ((b&0x00ff00ff)<<8);
+ row[j+8+i] =((t&0xff00ff00)>>8) | (b&0xff00ff00);
+ }
+ }
+ for(j=0;j<64;j+=8){
+ unsigned int t,b;
+ for(i=0;i<4;i++){
+ t=row[j+i];
+ b=row[j+4+i];
+ row[j+i] =((t&0xf0f0f0f0)>>4) | (b&0xf0f0f0f0);
+ row[j+4+i]= (t&0x0f0f0f0f) | ((b&0x0f0f0f0f)<<4);
+ }
+ }
+ for(j=0;j<64;j+=4){
+ unsigned int t,b;
+ for(i=0;i<2;i++){
+ t=row[j+i];
+ b=row[j+2+i];
+ row[j+i] =((t&0xcccccccc)>>2) | (b&0xcccccccc);
+ row[j+2+i]= (t&0x33333333) | ((b&0x33333333)<<2);
+ }
+ }
+ for(j=0;j<64;j+=2){
+ unsigned int t,b;
+ for(i=0;i<1;i++){
+ t=row[j+i];
+ b=row[j+1+i];
+ row[j+i] =((t&0xaaaaaaaa)>>1) | (b&0xaaaaaaaa);
+ row[j+1+i]= (t&0x55555555) | ((b&0x55555555)<<1);
+ }
+ }
+#undef row
+}
+
+//64-64----------------------------------------------------------
+static inline void trasp64_64_88ccw(unsigned char *data){
+/* 64 rows of 64 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/
+#define row ((unsigned long long int *)data)
+ int i,j;
+ for(j=0;j<64;j+=64){
+ unsigned long long int t,b;
+ for(i=0;i<32;i++){
+ t=row[j+i];
+ b=row[j+32+i];
+ row[j+i] = (t&0x00000000ffffffffULL) | ((b )<<32);
+ row[j+32+i]=((t )>>32) | (b&0xffffffff00000000ULL) ;
+ }
+ }
+ for(j=0;j<64;j+=32){
+ unsigned long long int t,b;
+ for(i=0;i<16;i++){
+ t=row[j+i];
+ b=row[j+16+i];
+ row[j+i] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16);
+ row[j+16+i]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ;
+ }
+ }
+ for(j=0;j<64;j+=16){
+ unsigned long long int t,b;
+ for(i=0;i<8;i++){
+ t=row[j+i];
+ b=row[j+8+i];
+ row[j+i] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8);
+ row[j+8+i] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL);
+ }
+ }
+ for(j=0;j<64;j+=8){
+ unsigned long long int t,b;
+ for(i=0;i<4;i++){
+ t=row[j+i];
+ b=row[j+4+i];
+ row[j+i] =((t&0x0f0f0f0f0f0f0f0fULL)<<4) | (b&0x0f0f0f0f0f0f0f0fULL);
+ row[j+4+i] = (t&0xf0f0f0f0f0f0f0f0ULL) | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4);
+ }
+ }
+ for(j=0;j<64;j+=4){
+ unsigned long long int t,b;
+ for(i=0;i<2;i++){
+ t=row[j+i];
+ b=row[j+2+i];
+ row[j+i] =((t&0x3333333333333333ULL)<<2) | (b&0x3333333333333333ULL);
+ row[j+2+i] = (t&0xccccccccccccccccULL) | ((b&0xccccccccccccccccULL)>>2);
+ }
+ }
+ for(j=0;j<64;j+=2){
+ unsigned long long int t,b;
+ for(i=0;i<1;i++){
+ t=row[j+i];
+ b=row[j+1+i];
+ row[j+i] =((t&0x5555555555555555ULL)<<1) | (b&0x5555555555555555ULL);
+ row[j+1+i] = (t&0xaaaaaaaaaaaaaaaaULL) | ((b&0xaaaaaaaaaaaaaaaaULL)>>1);
+ }
+ }
+#undef row
+}
+
+static inline void trasp64_64_88cw(unsigned char *data){
+/* 64 rows of 64 bits transposition (bytes transp. - 8x8 rotate clockwise)*/
+#define row ((unsigned long long int *)data)
+ int i,j;
+ for(j=0;j<64;j+=64){
+ unsigned long long int t,b;
+ for(i=0;i<32;i++){
+ t=row[j+i];
+ b=row[j+32+i];
+ row[j+i] = (t&0x00000000ffffffffULL) | ((b )<<32);
+ row[j+32+i]=((t )>>32) | (b&0xffffffff00000000ULL) ;
+ }
+ }
+ for(j=0;j<64;j+=32){
+ unsigned long long int t,b;
+ for(i=0;i<16;i++){
+ t=row[j+i];
+ b=row[j+16+i];
+ row[j+i] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16);
+ row[j+16+i]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ;
+ }
+ }
+ for(j=0;j<64;j+=16){
+ unsigned long long int t,b;
+ for(i=0;i<8;i++){
+ t=row[j+i];
+ b=row[j+8+i];
+ row[j+i] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8);
+ row[j+8+i] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL);
+ }
+ }
+ for(j=0;j<64;j+=8){
+ unsigned long long int t,b;
+ for(i=0;i<4;i++){
+ t=row[j+i];
+ b=row[j+4+i];
+ row[j+i] =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) | (b&0xf0f0f0f0f0f0f0f0ULL);
+ row[j+4+i] = (t&0x0f0f0f0f0f0f0f0fULL) | ((b&0x0f0f0f0f0f0f0f0fULL)<<4);
+ }
+ }
+ for(j=0;j<64;j+=4){
+ unsigned long long int t,b;
+ for(i=0;i<2;i++){
+ t=row[j+i];
+ b=row[j+2+i];
+ row[j+i] =((t&0xccccccccccccccccULL)>>2) | (b&0xccccccccccccccccULL);
+ row[j+2+i] = (t&0x3333333333333333ULL) | ((b&0x3333333333333333ULL)<<2);
+ }
+ }
+ for(j=0;j<64;j+=2){
+ unsigned long long int t,b;
+ for(i=0;i<1;i++){
+ t=row[j+i];
+ b=row[j+1+i];
+ row[j+i] =((t&0xaaaaaaaaaaaaaaaaULL)>>1) | (b&0xaaaaaaaaaaaaaaaaULL);
+ row[j+1+i] = (t&0x5555555555555555ULL) | ((b&0x5555555555555555ULL)<<1);
+ }
+ }
+#undef row
+}
+
+//64-128----------------------------------------------------------
+static inline void trasp64_128_88ccw(unsigned char *data){
+/* 64 rows of 128 bits transposition (bytes transp. - 8x8 rotate counterclockwise)*/
+#define halfrow ((unsigned long long int *)data)
+ int i,j;
+ for(j=0;j<64;j+=64){
+ unsigned long long int t,b;
+ for(i=0;i<32;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+32+i)];
+ halfrow[2*(j+i)] = (t&0x00000000ffffffffULL) | ((b )<<32);
+ halfrow[2*(j+32+i)]=((t )>>32) | (b&0xffffffff00000000ULL) ;
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+32+i)+1];
+ halfrow[2*(j+i)+1] = (t&0x00000000ffffffffULL) | ((b )<<32);
+ halfrow[2*(j+32+i)+1]=((t )>>32) | (b&0xffffffff00000000ULL) ;
+ }
+ }
+ for(j=0;j<64;j+=32){
+ unsigned long long int t,b;
+ for(i=0;i<16;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+16+i)];
+ halfrow[2*(j+i)] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16);
+ halfrow[2*(j+16+i)]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ;
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+16+i)+1];
+ halfrow[2*(j+i)+1] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16);
+ halfrow[2*(j+16+i)+1]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ;
+ }
+ }
+ for(j=0;j<64;j+=16){
+ unsigned long long int t,b;
+ for(i=0;i<8;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+8+i)];
+ halfrow[2*(j+i)] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8);
+ halfrow[2*(j+8+i)] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL);
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+8+i)+1];
+ halfrow[2*(j+i)+1] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8);
+ halfrow[2*(j+8+i)+1] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL);
+ }
+ }
+ for(j=0;j<64;j+=8){
+ unsigned long long int t,b;
+ for(i=0;i<4;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+4+i)];
+ halfrow[2*(j+i)] =((t&0x0f0f0f0f0f0f0f0fULL)<<4) | (b&0x0f0f0f0f0f0f0f0fULL);
+ halfrow[2*(j+4+i)] = (t&0xf0f0f0f0f0f0f0f0ULL) | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4);
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+4+i)+1];
+ halfrow[2*(j+i)+1] =((t&0x0f0f0f0f0f0f0f0fULL)<<4) | (b&0x0f0f0f0f0f0f0f0fULL);
+ halfrow[2*(j+4+i)+1] = (t&0xf0f0f0f0f0f0f0f0ULL) | ((b&0xf0f0f0f0f0f0f0f0ULL)>>4);
+ }
+ }
+ for(j=0;j<64;j+=4){
+ unsigned long long int t,b;
+ for(i=0;i<2;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+2+i)];
+ halfrow[2*(j+i)] =((t&0x3333333333333333ULL)<<2) | (b&0x3333333333333333ULL);
+ halfrow[2*(j+2+i)] = (t&0xccccccccccccccccULL) | ((b&0xccccccccccccccccULL)>>2);
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+2+i)+1];
+ halfrow[2*(j+i)+1] =((t&0x3333333333333333ULL)<<2) | (b&0x3333333333333333ULL);
+ halfrow[2*(j+2+i)+1] = (t&0xccccccccccccccccULL) | ((b&0xccccccccccccccccULL)>>2);
+ }
+ }
+ for(j=0;j<64;j+=2){
+ unsigned long long int t,b;
+ for(i=0;i<1;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+1+i)];
+ halfrow[2*(j+i)] =((t&0x5555555555555555ULL)<<1) | (b&0x5555555555555555ULL);
+ halfrow[2*(j+1+i)] = (t&0xaaaaaaaaaaaaaaaaULL) | ((b&0xaaaaaaaaaaaaaaaaULL)>>1);
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+1+i)+1];
+ halfrow[2*(j+i)+1] =((t&0x5555555555555555ULL)<<1) | (b&0x5555555555555555ULL);
+ halfrow[2*(j+1+i)+1] = (t&0xaaaaaaaaaaaaaaaaULL) | ((b&0xaaaaaaaaaaaaaaaaULL)>>1);
+ }
+ }
+#undef halfrow
+}
+
+static inline void trasp64_128_88cw(unsigned char *data){
+/* 64 rows of 128 bits transposition (bytes transp. - 8x8 rotate clockwise)*/
+#define halfrow ((unsigned long long int *)data)
+ int i,j;
+ for(j=0;j<64;j+=64){
+ unsigned long long int t,b;
+ for(i=0;i<32;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+32+i)];
+ halfrow[2*(j+i)] = (t&0x00000000ffffffffULL) | ((b )<<32);
+ halfrow[2*(j+32+i)]=((t )>>32) | (b&0xffffffff00000000ULL) ;
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+32+i)+1];
+ halfrow[2*(j+i)+1] = (t&0x00000000ffffffffULL) | ((b )<<32);
+ halfrow[2*(j+32+i)+1]=((t )>>32) | (b&0xffffffff00000000ULL) ;
+ }
+ }
+ for(j=0;j<64;j+=32){
+ unsigned long long int t,b;
+ for(i=0;i<16;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+16+i)];
+ halfrow[2*(j+i)] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16);
+ halfrow[2*(j+16+i)]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ;
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+16+i)+1];
+ halfrow[2*(j+i)+1] = (t&0x0000ffff0000ffffULL) | ((b&0x0000ffff0000ffffULL)<<16);
+ halfrow[2*(j+16+i)+1]=((t&0xffff0000ffff0000ULL)>>16) | (b&0xffff0000ffff0000ULL) ;
+ }
+ }
+ for(j=0;j<64;j+=16){
+ unsigned long long int t,b;
+ for(i=0;i<8;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+8+i)];
+ halfrow[2*(j+i)] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8);
+ halfrow[2*(j+8+i)] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL);
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+8+i)+1];
+ halfrow[2*(j+i)+1] = (t&0x00ff00ff00ff00ffULL) | ((b&0x00ff00ff00ff00ffULL)<<8);
+ halfrow[2*(j+8+i)+1] =((t&0xff00ff00ff00ff00ULL)>>8) | (b&0xff00ff00ff00ff00ULL);
+ }
+ }
+ for(j=0;j<64;j+=8){
+ unsigned long long int t,b;
+ for(i=0;i<4;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+4+i)];
+ halfrow[2*(j+i)] =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) | (b&0xf0f0f0f0f0f0f0f0ULL);
+ halfrow[2*(j+4+i)] = (t&0x0f0f0f0f0f0f0f0fULL) | ((b&0x0f0f0f0f0f0f0f0fULL)<<4);
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+4+i)+1];
+ halfrow[2*(j+i)+1] =((t&0xf0f0f0f0f0f0f0f0ULL)>>4) | (b&0xf0f0f0f0f0f0f0f0ULL);
+ halfrow[2*(j+4+i)+1] = (t&0x0f0f0f0f0f0f0f0fULL) | ((b&0x0f0f0f0f0f0f0f0fULL)<<4);
+ }
+ }
+ for(j=0;j<64;j+=4){
+ unsigned long long int t,b;
+ for(i=0;i<2;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+2+i)];
+ halfrow[2*(j+i)] =((t&0xccccccccccccccccULL)>>2) | (b&0xccccccccccccccccULL);
+ halfrow[2*(j+2+i)] = (t&0x3333333333333333ULL) | ((b&0x3333333333333333ULL)<<2);
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+2+i)+1];
+ halfrow[2*(j+i)+1] =((t&0xccccccccccccccccULL)>>2) | (b&0xccccccccccccccccULL);
+ halfrow[2*(j+2+i)+1] = (t&0x3333333333333333ULL) | ((b&0x3333333333333333ULL)<<2);
+ }
+ }
+ for(j=0;j<64;j+=2){
+ unsigned long long int t,b;
+ for(i=0;i<1;i++){
+ t=halfrow[2*(j+i)];
+ b=halfrow[2*(j+1+i)];
+ halfrow[2*(j+i)] =((t&0xaaaaaaaaaaaaaaaaULL)>>1) | (b&0xaaaaaaaaaaaaaaaaULL);
+ halfrow[2*(j+1+i)] = (t&0x5555555555555555ULL) | ((b&0x5555555555555555ULL)<<1);
+ t=halfrow[2*(j+i)+1];
+ b=halfrow[2*(j+1+i)+1];
+ halfrow[2*(j+i)+1] =((t&0xaaaaaaaaaaaaaaaaULL)>>1) | (b&0xaaaaaaaaaaaaaaaaULL);
+ halfrow[2*(j+1+i)+1] = (t&0x5555555555555555ULL) | ((b&0x5555555555555555ULL)<<1);
+ }
+ }
+#undef halfrow
+}
+#endif
+
+
+#ifdef STREAM_INIT
+void stream_cypher_group_init(
+ struct stream_regs *regs,
+ group iA[8][4], // [In] iA00,iA01,...iA73 32 groups | Derived from key.
+ group iB[8][4], // [In] iB00,iB01,...iB73 32 groups | Derived from key.
+ unsigned char *sb) // [In] (SB0,SB1,...SB7)...x32 32*8 bytes | Extra input.
+#endif
+#ifdef STREAM_NORMAL
+void stream_cypher_group_normal(
+ struct stream_regs *regs,
+ unsigned char *cb) // [Out] (CB0,CB1,...CB7)...x32 32*8 bytes | Output.
+#endif
+{
+#ifdef STREAM_INIT
+ group in1[4];
+ group in2[4];
+#endif
+ group extra_B[4];
+ group fa,fb,fc,fd,fe;
+ group s1a,s1b,s2a,s2b,s3a,s3b,s4a,s4b,s5a,s5b,s6a,s6b,s7a,s7b;
+ group next_E[4];
+ group tmp0,tmp1,tmp2,tmp3,tmp4;
+#ifdef STREAM_INIT
+ group *sb_g=(group *)sb;
+#endif
+#ifdef STREAM_NORMAL
+ group *cb_g=(group *)cb;
+#endif
+ int aboff;
+ int i,j,k,b;
+ int dbg;
+
+#ifdef STREAM_INIT
+ DBG(fprintf(stderr,":::::::::: BEGIN STREAM INIT\n"));
+#endif
+#ifdef STREAM_NORMAL
+ DBG(fprintf(stderr,":::::::::: BEGIN STREAM NORMAL\n"));
+#endif
+#ifdef STREAM_INIT
+for(j=0;j<64;j++){
+ DBG(fprintf(stderr,"precall prerot stream_in[%2i]=",j));
+ DBG(dump_mem("",sb+BYPG*j,BYPG,BYPG));
+}
+
+DBG(dump_mem("stream_prerot ",sb,GROUP_PARALLELISM*8,BYPG));
+#if GROUP_PARALLELISM==32
+trasp64_32_88ccw(sb);
+#endif
+#if GROUP_PARALLELISM==64
+trasp64_64_88ccw(sb);
+#endif
+#if GROUP_PARALLELISM==128
+trasp64_128_88ccw(sb);
+#endif
+DBG(dump_mem("stream_postrot",sb,GROUP_PARALLELISM*8,BYPG));
+
+for(j=0;j<64;j++){
+ DBG(fprintf(stderr,"precall stream_in[%2i]=",j));
+ DBG(dump_mem("",sb+BYPG*j,BYPG,BYPG));
+}
+#endif
+
+ aboff=32;
+
+#ifdef STREAM_INIT
+ // load first 32 bits of ck into A[aboff+0]..A[aboff+7]
+ // load last 32 bits of ck into B[aboff+0]..B[aboff+7]
+ // all other regs = 0
+ for(i=0;i<8;i++){
+ for(b=0;b<4;b++){
+DBG(fprintf(stderr,"dbg from iA A[%i][%i]=",i,b));
+DBG(dump_mem("",(unsigned char *)&iA[i][b],BYPG,BYPG));
+DBG(fprintf(stderr," dbg from iB B[%i][%i]=",i,b));
+DBG(dump_mem("",(unsigned char *)&iB[i][b],BYPG,BYPG));
+ regs->A[aboff+i][b]=iA[i][b];
+ regs->B[aboff+i][b]=iB[i][b];
+ }
+ }
+ for(b=0;b<4;b++){
+ regs->A[aboff+8][b]=FF0();
+ regs->A[aboff+9][b]=FF0();
+ regs->B[aboff+8][b]=FF0();
+ regs->B[aboff+9][b]=FF0();
+ }
+ for(b=0;b<4;b++){
+ regs->X[b]=FF0();
+ regs->Y[b]=FF0();
+ regs->Z[b]=FF0();
+ regs->D[b]=FF0();
+ regs->E[b]=FF0();
+ regs->F[b]=FF0();
+ }
+ regs->p=FF0();
+ regs->q=FF0();
+ regs->r=FF0();
+#endif
+
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"dbg A0[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->A[aboff+0][dbg],BYPG,BYPG));
+ DBG(fprintf(stderr,"dbg B0[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->B[aboff+0][dbg],BYPG,BYPG));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ // EXTERNAL LOOP - 8 bytes per operation
+ for(i=0;i<8;i++){
+
+ DBG(fprintf(stderr,"--BEGIN EXTERNAL LOOP %i\n",i));
+
+#ifdef STREAM_INIT
+ for(b=0;b<4;b++){
+ in1[b]=sb_g[8*i+4+b];
+ in2[b]=sb_g[8*i+b];
+ }
+#endif
+
+ // INTERNAL LOOP - 2 bits per iteration
+ for(j=0; j<4; j++){
+
+ DBG(fprintf(stderr,"---BEGIN INTERNAL LOOP %i (EXT %i, INT %i)\n",j,i,j));
+
+ // from A0..A9, 35 bits are selected as inputs to 7 s-boxes
+ // 5 bits input per s-box, 2 bits output per s-box
+
+ // we can select bits with zero masking and shifting operations
+ // and synthetize s-boxes with optimized boolean functions.
+ // this is the actual reason we do all the crazy transposition
+ // stuff to switch between normal and bit slice representations.
+ // this code really flies.
+
+ fe=regs->A[aboff+3][0];fa=regs->A[aboff+0][2];fb=regs->A[aboff+5][1];fc=regs->A[aboff+6][3];fd=regs->A[aboff+8][0];
+/* 1000 1110 1110 0001 : lev 7: */ //tmp0=( fa^( fb^( ( ( ( fa|fb )^fc )|( fc^fd ) )^ALL_ONES ) ) );
+/* 1110 0010 0011 0011 : lev 6: */ //tmp1=( ( fa|fb )^( ( fc&( fa|( fb^fd ) ) )^ALL_ONES ) );
+/* 0011 0110 1000 1101 : lev 5: */ //tmp2=( fa^( ( fb&fd )^( ( fa&fd )|fc ) ) );
+/* 0101 0101 1001 0011 : lev 5: */ //tmp3=( ( fa&fc )^( fa^( ( fa&fb )|fd ) ) );
+/* 1000 1110 1110 0001 : lev 7: */ tmp0=FFXOR(fa,FFXOR(fb,FFXOR(FFOR(FFXOR(FFOR(fa,fb),fc),FFXOR(fc,fd)),FF1())));
+/* 1110 0010 0011 0011 : lev 6: */ tmp1=FFXOR(FFOR(fa,fb),FFXOR(FFAND(fc,FFOR(fa,FFXOR(fb,fd))),FF1()));
+/* 0011 0110 1000 1101 : lev 5: */ tmp2=FFXOR(fa,FFXOR(FFAND(fb,fd),FFOR(FFAND(fa,fd),fc)));
+/* 0101 0101 1001 0011 : lev 5: */ tmp3=FFXOR(FFAND(fa,fc),FFXOR(fa,FFOR(FFAND(fa,fb),fd)));
+ s1a=FFXOR(tmp0,FFAND(fe,tmp1));
+ s1b=FFXOR(tmp2,FFAND(fe,tmp3));
+//dump_mem("s1as1b-fe",&fe,BYPG,BYPG);
+//dump_mem("s1as1b-fa",&fa,BYPG,BYPG);
+//dump_mem("s1as1b-fb",&fb,BYPG,BYPG);
+//dump_mem("s1as1b-fc",&fc,BYPG,BYPG);
+//dump_mem("s1as1b-fd",&fd,BYPG,BYPG);
+
+ fe=regs->A[aboff+1][1];fa=regs->A[aboff+2][2];fb=regs->A[aboff+5][3];fc=regs->A[aboff+6][0];fd=regs->A[aboff+8][1];
+/* 1001 1110 0110 0001 : lev 6: */ //tmp0=( fa^( ( fb&( fc|fd ) )^( fc^( fd^ALL_ONES ) ) ) );
+/* 0000 0011 0111 1011 : lev 5: */ //tmp1=( ( fa&( fb^fd ) )|( ( fa|fb )&fc ) );
+/* 1100 0110 1101 0010 : lev 6: */ //tmp2=( ( fb&fd )^( ( fa&fd )|( fb^( fc^ALL_ONES ) ) ) );
+/* 0001 1110 1111 0101 : lev 5: */ //tmp3=( ( fa&fd )|( fa^( fb^( fc&fd ) ) ) );
+/* 1001 1110 0110 0001 : lev 6: */ tmp0=FFXOR(fa,FFXOR(FFAND(fb,FFOR(fc,fd)),FFXOR(fc,FFXOR(fd,FF1()))));
+/* 0000 0011 0111 1011 : lev 5: */ tmp1=FFOR(FFAND(fa,FFXOR(fb,fd)),FFAND(FFOR(fa,fb),fc));
+/* 1100 0110 1101 0010 : lev 6: */ tmp2=FFXOR(FFAND(fb,fd),FFOR(FFAND(fa,fd),FFXOR(fb,FFXOR(fc,FF1()))));
+/* 0001 1110 1111 0101 : lev 5: */ tmp3=FFOR(FFAND(fa,fd),FFXOR(fa,FFXOR(fb,FFAND(fc,fd))));
+ s2a=FFXOR(tmp0,FFAND(fe,tmp1));
+ s2b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+ fe=regs->A[aboff+0][3];fa=regs->A[aboff+1][0];fb=regs->A[aboff+4][1];fc=regs->A[aboff+4][3];fd=regs->A[aboff+5][2];
+/* 0100 1011 1001 0110 : lev 5: */ //tmp0=( fa^( fb^( ( fc&( fa|fd ) )^fd ) ) );
+/* 1101 0101 1000 1100 : lev 7: */ //tmp1=( ( fa&fc )^( ( fa^fd )|( ( fb|fc )^( fd^ALL_ONES ) ) ) );
+/* 0010 0111 1101 1000 : lev 4: */ //tmp2=( fa^( ( ( fb^fc )&fd )^fc ) );
+/* 1111 1111 1111 1111 : lev 0: */ //tmp3=ALL_ONES;
+/* 0100 1011 1001 0110 : lev 5: */ tmp0=FFXOR(fa,FFXOR(fb,FFXOR(FFAND(fc,FFOR(fa,fd)),fd)));
+/* 1101 0101 1000 1100 : lev 7: */ tmp1=FFXOR(FFAND(fa,fc),FFOR(FFXOR(fa,fd),FFXOR(FFOR(fb,fc),FFXOR(fd,FF1()))));
+/* 0010 0111 1101 1000 : lev 4: */ tmp2=FFXOR(fa,FFXOR(FFAND(FFXOR(fb,fc),fd),fc));
+/* 1111 1111 1111 1111 : lev 0: */ tmp3=FF1();
+ s3a=FFXOR(tmp0,FFAND(FFNOT(fe),tmp1));
+ s3b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+ fe=regs->A[aboff+2][3];fa=regs->A[aboff+0][1];fb=regs->A[aboff+1][3];fc=regs->A[aboff+3][2];fd=regs->A[aboff+7][0];
+/* 1011 0101 0100 1001 : lev 7: */ //tmp0=( fa^( ( fc&( fa^fd ) )|( fb^( fc|( fd^ALL_ONES ) ) ) ) );
+/* 0010 1101 0110 0110 : lev 6: */ //tmp1=( ( fa&fb )^( fb^( ( ( fa|fc )&fd )^fc ) ) );
+/* 0110 0111 1101 0000 : lev 7: */ //tmp2=( fa^( ( fb&fc )|( ( ( fa&( fb^fd ) )|fc )^fd ) ) );
+/* 1111 1111 1111 1111 : lev 0: */ //tmp3=ALL_ONES;
+/* 1011 0101 0100 1001 : lev 7: */ tmp0=FFXOR(fa,FFOR(FFAND(fc,FFXOR(fa,fd)),FFXOR(fb,FFOR(fc,FFXOR(fd,FF1())))));
+/* 0010 1101 0110 0110 : lev 6: */ tmp1=FFXOR(FFAND(fa,fb),FFXOR(fb,FFXOR(FFAND(FFOR(fa,fc),fd),fc)));
+/* 0110 0111 1101 0000 : lev 7: */ tmp2=FFXOR(fa,FFOR(FFAND(fb,fc),FFXOR(FFOR(FFAND(fa,FFXOR(fb,fd)),fc),fd)));
+/* 1111 1111 1111 1111 : lev 0: */ tmp3=FF1();
+ s4a=FFXOR(tmp0,FFAND(fe,FFXOR(tmp1,tmp0)));
+ s4b=FFXOR(FFXOR(s4a,tmp2),FFAND(fe,tmp3));
+
+ fe=regs->A[aboff+4][2];fa=regs->A[aboff+3][3];fb=regs->A[aboff+5][0];fc=regs->A[aboff+7][1];fd=regs->A[aboff+8][2];
+/* 1000 1111 0011 0010 : lev 7: */ //tmp0=( ( ( fa&( fb|fc ) )^fb )|( ( ( fa^fc )|fd )^ALL_ONES ) );
+/* 0110 1011 0000 1011 : lev 6: */ //tmp1=( fb^( ( fc^fd )&( fc^( fb|( fa^fd ) ) ) ) );
+/* 0001 1010 0111 1001 : lev 6: */ //tmp2=( ( fa&fc )^( fb^( ( fb|( fa^fc ) )&fd ) ) );
+/* 0101 1101 1101 0101 : lev 4: */ //tmp3=( ( ( fa^fb )&( fc^ALL_ONES ) )|fd );
+/* 1000 1111 0011 0010 : lev 7: */ tmp0=FFOR(FFXOR(FFAND(fa,FFOR(fb,fc)),fb),FFXOR(FFOR(FFXOR(fa,fc),fd),FF1()));
+/* 0110 1011 0000 1011 : lev 6: */ tmp1=FFXOR(fb,FFAND(FFXOR(fc,fd),FFXOR(fc,FFOR(fb,FFXOR(fa,fd)))));
+/* 0001 1010 0111 1001 : lev 6: */ tmp2=FFXOR(FFAND(fa,fc),FFXOR(fb,FFAND(FFOR(fb,FFXOR(fa,fc)),fd)));
+/* 0101 1101 1101 0101 : lev 4: */ tmp3=FFOR(FFAND(FFXOR(fa,fb),FFXOR(fc,FF1())),fd);
+ s5a=FFXOR(tmp0,FFAND(fe,tmp1));
+ s5b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+ fe=regs->A[aboff+2][1];fa=regs->A[aboff+3][1];fb=regs->A[aboff+4][0];fc=regs->A[aboff+6][2];fd=regs->A[aboff+8][3];
+/* 0011 0110 0010 1101 : lev 6: */ //tmp0=( ( ( fa&fc )&fd )^( ( fb&( fa|fd ) )^fc ) );
+/* 1110 1110 1011 1011 : lev 3: */ //tmp1=( ( ( fa^fc )&fd )^ALL_ONES );
+/* 0101 1000 0110 0111 : lev 6: */ //tmp2=( ( fa&( fb|fc ) )^( fb^( ( fb&fc )|fd ) ) );
+/* 0001 0011 0000 0001 : lev 5: */ //tmp3=( fc&( ( fa&( fb^fd ) )^( fb|fd ) ) );
+/* 0011 0110 0010 1101 : lev 6: */ tmp0=FFXOR(FFAND(FFAND(fa,fc),fd),FFXOR(FFAND(fb,FFOR(fa,fd)),fc));
+/* 1110 1110 1011 1011 : lev 3: */ tmp1=FFXOR(FFAND(FFXOR(fa,fc),fd),FF1());
+/* 0101 1000 0110 0111 : lev 6: */ tmp2=FFXOR(FFAND(fa,FFOR(fb,fc)),FFXOR(fb,FFOR(FFAND(fb,fc),fd)));
+/* 0001 0011 0000 0001 : lev 5: */ tmp3=FFAND(fc,FFXOR(FFAND(fa,FFXOR(fb,fd)),FFOR(fb,fd)));
+ s6a=FFXOR(tmp0,FFAND(fe,tmp1));
+ s6b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+ fe=regs->A[aboff+1][2];fa=regs->A[aboff+2][0];fb=regs->A[aboff+6][1];fc=regs->A[aboff+7][2];fd=regs->A[aboff+7][3];
+/* 0111 1000 1001 0110 : lev 5: */ //tmp0=( fb^( ( fc&fd )|( fa^( fc^fd ) ) ) );
+/* 0100 1001 0101 1011 : lev 6: */ //tmp1=( ( fb|fd )&( ( fa&fc )|( fb^( fc^fd ) ) ) );
+/* 0100 1001 1011 1001 : lev 5: */ //tmp2=( ( fa|fb )^( ( fc&( fb|fd ) )^fd ) );
+/* 1111 1111 1101 1101 : lev 3: */ //tmp3=( fd|( ( fa&fc )^ALL_ONES ) );
+/* 0111 1000 1001 0110 : lev 5: */ tmp0=FFXOR(fb,FFOR(FFAND(fc,fd),FFXOR(fa,FFXOR(fc,fd))));
+/* 0100 1001 0101 1011 : lev 6: */ tmp1=FFAND(FFOR(fb,fd),FFOR(FFAND(fa,fc),FFXOR(fb,FFXOR(fc,fd))));
+/* 0100 1001 1011 1001 : lev 5: */ tmp2=FFXOR(FFOR(fa,fb),FFXOR(FFAND(fc,FFOR(fb,fd)),fd));
+/* 1111 1111 1101 1101 : lev 3: */ tmp3=FFOR(fd,FFXOR(FFAND(fa,fc),FF1()));
+ s7a=FFXOR(tmp0,FFAND(fe,tmp1));
+ s7b=FFXOR(tmp2,FFAND(fe,tmp3));
+
+
+/*
+ we have just done this:
+
+ int sbox1[0x20] = {2,0,1,1,2,3,3,0, 3,2,2,0,1,1,0,3, 0,3,3,0,2,2,1,1, 2,2,0,3,1,1,3,0};
+ int sbox2[0x20] = {3,1,0,2,2,3,3,0, 1,3,2,1,0,0,1,2, 3,1,0,3,3,2,0,2, 0,0,1,2,2,1,3,1};
+ int sbox3[0x20] = {2,0,1,2,2,3,3,1, 1,1,0,3,3,0,2,0, 1,3,0,1,3,0,2,2, 2,0,1,2,0,3,3,1};
+ int sbox4[0x20] = {3,1,2,3,0,2,1,2, 1,2,0,1,3,0,0,3, 1,0,3,1,2,3,0,3, 0,3,2,0,1,2,2,1};
+ int sbox5[0x20] = {2,0,0,1,3,2,3,2, 0,1,3,3,1,0,2,1, 2,3,2,0,0,3,1,1, 1,0,3,2,3,1,0,2};
+ int sbox6[0x20] = {0,1,2,3,1,2,2,0, 0,1,3,0,2,3,1,3, 2,3,0,2,3,0,1,1, 2,1,1,2,0,3,3,0};
+ int sbox7[0x20] = {0,3,2,2,3,0,0,1, 3,0,1,3,1,2,2,1, 1,0,3,3,0,1,1,2, 2,3,1,0,2,3,0,2};
+
+ s12 = sbox1[ (((A3>>0)&1)<<4) | (((A0>>2)&1)<<3) | (((A5>>1)&1)<<2) | (((A6>>3)&1)<<1) | (((A8>>0)&1)<<0) ]
+ |sbox2[ (((A1>>1)&1)<<4) | (((A2>>2)&1)<<3) | (((A5>>3)&1)<<2) | (((A6>>0)&1)<<1) | (((A8>>1)&1)<<0) ];
+ s34 = sbox3[ (((A0>>3)&1)<<4) | (((A1>>0)&1)<<3) | (((A4>>1)&1)<<2) | (((A4>>3)&1)<<1) | (((A5>>2)&1)<<0) ]
+ |sbox4[ (((A2>>3)&1)<<4) | (((A0>>1)&1)<<3) | (((A1>>3)&1)<<2) | (((A3>>2)&1)<<1) | (((A7>>0)&1)<<0) ];
+ s56 = sbox5[ (((A4>>2)&1)<<4) | (((A3>>3)&1)<<3) | (((A5>>0)&1)<<2) | (((A7>>1)&1)<<1) | (((A8>>2)&1)<<0) ]
+ |sbox6[ (((A2>>1)&1)<<4) | (((A3>>1)&1)<<3) | (((A4>>0)&1)<<2) | (((A6>>2)&1)<<1) | (((A8>>3)&1)<<0) ];
+ s7 = sbox7[ (((A1>>2)&1)<<4) | (((A2>>0)&1)<<3) | (((A6>>1)&1)<<2) | (((A7>>2)&1)<<1) | (((A7>>3)&1)<<0) ];
+*/
+
+ // use 4x4 xor to produce extra nibble for T3
+
+ extra_B[3]=FFXOR(FFXOR(FFXOR(regs->B[aboff+2][0],regs->B[aboff+5][1]),regs->B[aboff+6][2]),regs->B[aboff+8][3]);
+ extra_B[2]=FFXOR(FFXOR(FFXOR(regs->B[aboff+5][0],regs->B[aboff+7][1]),regs->B[aboff+2][3]),regs->B[aboff+3][2]);
+ extra_B[1]=FFXOR(FFXOR(FFXOR(regs->B[aboff+4][3],regs->B[aboff+7][2]),regs->B[aboff+3][0]),regs->B[aboff+4][1]);
+ extra_B[0]=FFXOR(FFXOR(FFXOR(regs->B[aboff+8][2],regs->B[aboff+5][3]),regs->B[aboff+2][1]),regs->B[aboff+7][0]);
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"extra_B[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)&extra_B[dbg],BYPG,BYPG));
+}
+
+ // T1 = xor all inputs
+ // in1, in2, D are only used in T1 during initialisation, not generation
+ for(b=0;b<4;b++){
+ regs->A[aboff-1][b]=FFXOR(regs->A[aboff+9][b],regs->X[b]);
+ }
+
+#ifdef STREAM_INIT
+ for(b=0;b<4;b++){
+ regs->A[aboff-1][b]=FFXOR(FFXOR(regs->A[aboff-1][b],regs->D[b]),((j % 2) ? in2[b] : in1[b]));
+ }
+#endif
+
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"next_A0[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->A[aboff-1][dbg],BYPG,BYPG));
+}
+
+ // T2 = xor all inputs
+ // in1, in2 are only used in T1 during initialisation, not generation
+ // if p=0, use this, if p=1, rotate the result left
+ for(b=0;b<4;b++){
+ regs->B[aboff-1][b]=FFXOR(FFXOR(regs->B[aboff+6][b],regs->B[aboff+9][b]),regs->Y[b]);
+ }
+
+#ifdef STREAM_INIT
+ for(b=0;b<4;b++){
+ regs->B[aboff-1][b]=FFXOR(regs->B[aboff-1][b],((j % 2) ? in1[b] : in2[b]));
+ }
+#endif
+
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"next_B0[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->B[aboff-1][dbg],BYPG,BYPG));
+}
+
+ // if p=1, rotate left (yes, this is what we're doing)
+ tmp3=regs->B[aboff-1][3];
+ regs->B[aboff-1][3]=FFXOR(regs->B[aboff-1][3],FFAND(FFXOR(regs->B[aboff-1][3],regs->B[aboff-1][2]),regs->p));
+ regs->B[aboff-1][2]=FFXOR(regs->B[aboff-1][2],FFAND(FFXOR(regs->B[aboff-1][2],regs->B[aboff-1][1]),regs->p));
+ regs->B[aboff-1][1]=FFXOR(regs->B[aboff-1][1],FFAND(FFXOR(regs->B[aboff-1][1],regs->B[aboff-1][0]),regs->p));
+ regs->B[aboff-1][0]=FFXOR(regs->B[aboff-1][0],FFAND(FFXOR(regs->B[aboff-1][0],tmp3),regs->p));
+
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"next_B0[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->B[aboff-1][dbg],BYPG,BYPG));
+}
+
+ // T3 = xor all inputs
+ for(b=0;b<4;b++){
+ regs->D[b]=FFXOR(FFXOR(regs->E[b],regs->Z[b]),extra_B[b]);
+ }
+
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"D[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->D[dbg],BYPG,BYPG));
+}
+
+ // T4 = sum, carry of Z + E + r
+ for(b=0;b<4;b++){
+ next_E[b]=regs->F[b];
+ }
+
+ tmp0=FFXOR(regs->Z[0],regs->E[0]);
+ tmp1=FFAND(regs->Z[0],regs->E[0]);
+ regs->F[0]=FFXOR(regs->E[0],FFAND(regs->q,FFXOR(regs->Z[0],regs->r)));
+ tmp3=FFAND(tmp0,regs->r);
+ tmp4=FFOR(tmp1,tmp3);
+
+ tmp0=FFXOR(regs->Z[1],regs->E[1]);
+ tmp1=FFAND(regs->Z[1],regs->E[1]);
+ regs->F[1]=FFXOR(regs->E[1],FFAND(regs->q,FFXOR(regs->Z[1],tmp4)));
+ tmp3=FFAND(tmp0,tmp4);
+ tmp4=FFOR(tmp1,tmp3);
+
+ tmp0=FFXOR(regs->Z[2],regs->E[2]);
+ tmp1=FFAND(regs->Z[2],regs->E[2]);
+ regs->F[2]=FFXOR(regs->E[2],FFAND(regs->q,FFXOR(regs->Z[2],tmp4)));
+ tmp3=FFAND(tmp0,tmp4);
+ tmp4=FFOR(tmp1,tmp3);
+
+ tmp0=FFXOR(regs->Z[3],regs->E[3]);
+ tmp1=FFAND(regs->Z[3],regs->E[3]);
+ regs->F[3]=FFXOR(regs->E[3],FFAND(regs->q,FFXOR(regs->Z[3],tmp4)));
+ tmp3=FFAND(tmp0,tmp4);
+ regs->r=FFXOR(regs->r,FFAND(regs->q,FFXOR(FFOR(tmp1,tmp3),regs->r))); // ultimate carry
+
+/*
+ we have just done this: (believe it or not)
+
+ if (q) {
+ F = Z + E + r;
+ r = (F >> 4) & 1;
+ F = F & 0x0f;
+ }
+ else {
+ F = E;
+ }
+*/
+ for(b=0;b<4;b++){
+ regs->E[b]=next_E[b];
+ }
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"F[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->F[dbg],BYPG,BYPG));
+}
+DBG(fprintf(stderr,"r="));
+DBG(dump_mem("",(unsigned char *)®s->r,BYPG,BYPG));
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"E[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->E[dbg],BYPG,BYPG));
+}
+
+ // this simple instruction is virtually shifting all the shift registers
+ aboff--;
+
+/*
+ we've just done this:
+
+ A9=A8;A8=A7;A7=A6;A6=A5;A5=A4;A4=A3;A3=A2;A2=A1;A1=A0;A0=next_A0;
+ B9=B8;B8=B7;B7=B6;B6=B5;B5=B4;B4=B3;B3=B2;B2=B1;B1=B0;B0=next_B0;
+*/
+
+ regs->X[0]=s1a;
+ regs->X[1]=s2a;
+ regs->X[2]=s3b;
+ regs->X[3]=s4b;
+ regs->Y[0]=s3a;
+ regs->Y[1]=s4a;
+ regs->Y[2]=s5b;
+ regs->Y[3]=s6b;
+ regs->Z[0]=s5a;
+ regs->Z[1]=s6a;
+ regs->Z[2]=s1b;
+ regs->Z[3]=s2b;
+ regs->p=s7a;
+ regs->q=s7b;
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"X[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->X[dbg],BYPG,BYPG));
+}
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"Y[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->Y[dbg],BYPG,BYPG));
+}
+for(dbg=0;dbg<4;dbg++){
+ DBG(fprintf(stderr,"Z[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)®s->Z[dbg],BYPG,BYPG));
+}
+DBG(fprintf(stderr,"p="));
+DBG(dump_mem("",(unsigned char *)®s->p,BYPG,BYPG));
+DBG(fprintf(stderr,"q="));
+DBG(dump_mem("",(unsigned char *)®s->q,BYPG,BYPG));
+
+#ifdef STREAM_NORMAL
+ // require 4 loops per output byte
+ // 2 output bits are a function of the 4 bits of D
+ // xor 2 by 2
+ cb_g[8*i+7-2*j]=FFXOR(regs->D[2],regs->D[3]);
+ cb_g[8*i+6-2*j]=FFXOR(regs->D[0],regs->D[1]);
+for(dbg=0;dbg<8;dbg++){
+ DBG(fprintf(stderr,"op[%i]=",dbg));
+ DBG(dump_mem("",(unsigned char *)&cb_g[8*i+dbg],BYPG,BYPG));
+}
+#endif
+
+DBG(fprintf(stderr,"---END INTERNAL LOOP\n"));
+
+ } // INTERNAL LOOP
+
+DBG(fprintf(stderr,"--END EXTERNAL LOOP\n"));
+
+ } // EXTERNAL LOOP
+
+ // move 32 steps forward, ready for next call
+ for(k=0;k<10;k++){
+ for(b=0;b<4;b++){
+DBG(fprintf(stderr,"moving forward AB k=%i b=%i\n",k,b));
+ regs->A[32+k][b]=regs->A[k][b];
+ regs->B[32+k][b]=regs->B[k][b];
+ }
+ }
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+#ifdef STREAM_NORMAL
+for(j=0;j<64;j++){
+ DBG(fprintf(stderr,"postcall prerot cb[%2i]=",j));
+ DBG(dump_mem("",(unsigned char *)(cb+BYPG*j),BYPG,BYPG));
+}
+
+#if GROUP_PARALLELISM==32
+trasp64_32_88cw(cb);
+#endif
+#if GROUP_PARALLELISM==64
+trasp64_64_88cw(cb);
+#endif
+#if GROUP_PARALLELISM==128
+trasp64_128_88cw(cb);
+#endif
+
+for(j=0;j<64;j++){
+ DBG(fprintf(stderr,"postcall postrot cb[%2i]=",j));
+ DBG(dump_mem("",(unsigned char *)(cb+BYPG*j),BYPG,BYPG));
+}
+#endif
+
+#ifdef STREAM_INIT
+ DBG(fprintf(stderr,":::::::::: END STREAM INIT\n"));
+#endif
+#ifdef STREAM_NORMAL
+ DBG(fprintf(stderr,":::::::::: END STREAM NORMAL\n"));
+#endif
+
+}
+
--- /dev/null
+VDR Plugin 'sc' Revision History
+--------------------------------
+
+22.06.2007: Version 0.8.0
+- Added support for Nagra smartcards. Code is not well tested. Volunteers
+ welcome. Timing is tricky, so you have to get cardreader clock AND -C
+ parameter right.
+- Added debug description to CI adapter ringbuffers.
+- Added a note to README that the plugin have to be put first on VDR
+ commandline. Loading certain plugins (e.g. softdevice) in front of SC leads to
+ mismatched device numbering in VDR which causes some strange effects.
+- Added sanity check for device numbering.
+- Fixed Nagra2 nano processing.
+- Fixed Seca EMM signature check.
+- Never ending story: fixed cardclient reconnecting on read timeout again.
+- Fixed cardclient camd35 sending Nagra provider in ECM request.
+- Added russian translations (core only).
+
+------------------------------------------------------------
+
+13.05.2007: Version 0.7.5
+- Fixed memcpy race in OpenTV decompress.
+- Fixed endless loop with evil EMM data in Nagra2 0101 B1 code.
+- Fixed access to disabled DVB cards (1.4.x).
+- Fixed budget card problem in VDR core patch (1.4.x).
+
+06.05.2007: Version 0.7.4
+- Added compatibility for VDR 1.4.6 (VDR core has to be patched). Note that this
+ is experimental code.
+- Added support for new TPS AU sheme.
+- Added some Map handling to DN/BEV B1 processing code.
+- Added version information to hello message in cardclient radegast.
+- Added logic to Makefile to copy max. number of CAIDs from VDR.
+- Extended ConstCW key to handle cable/terrestrial sources.
+- Fixed race in camslot reset code.
+- Fixed zero-cw-index handling for CI update messages.
+- Fixed handling of same SID on different transponders in lru-caid-cache.
+- Fixed menu processing on ECM cache flush abort.
+
+06.04.2007: Version 0.7.3
+- This is a beta release. As the code seems pretty stable, we would like to
+ encourage everybody to try this release. Nevertheless it should be used under
+ controlled conditions only.
+- Further improved camslot reset behaviour. Toggling concurrent flag takes
+ effect at runtime now too.
+- Removed obsolete D+ AU code.
+- Fixed unexpect side effect of cardclient reconnecting on read timeout.
+- Fixed CAID allocation (CheckIgnore).
+
+31.03.2007: Version 0.7.2
+- Now creating the devices nodes after loading setup.conf. Should solve problems
+ with SourceCaps patch and remove the "nextCardIndex to big" error. But this
+ requires some ugly hacking which may not work with every gcc/vdr version.
+- Added DarkAvengers FFdecsa optimizations for mmx, sse and sse2 modes.
+- Added SVDR command to display all message classes.
+- Added new D+ AU. You need an additional NN 52 RSA key (and optional NN 53
+ verify key).
+- Added autodetection of pid where TPS broadcasts AU data. You still have to
+ switch to transponder 10873 for AU.
+- Changed the plugin shutdown sequence to fix the hang-on-exit problem. It seems
+ to be fixed now, although the reason for the problem is still unknown.
+- Fixed Nagra2 0101 map 4d input.
+- Fixed logging Nagra cpu emu messages to general.unknown.
+- Fixed reseting module options to default.
+- Fixed a mismatched Lock/Unlock in smartcard code (in error path).
+- Fixed off-by-one error in CI adapter read.
+- Fixed camslot reset logic.
+- Fixed some 'valgrind' problems and properly unload all sub-libraries on plugin
+ exit too.
+- Updated README documentation, removed README.0.7.x.
+- Updated finnish translations.
+
+09.03.2007: Version 0.7.1
+- Reduced the number of used camslots to 1 per device. Multiply camslots cause
+ all kinds of problems. Reworked CAID allocation algorithm. This should make
+ operation much more reliable.
+- Improved operation with hardware CAM.
+- Introducing a completely new message logging system. All messages classes can
+ be enabled/disabled at runtime (via setup menu and SVDR) and can be written to
+ console, file and/or syslog (configurable from setup menu).
+- Added Nagra2 provider 0101 Map57 call.
+- Added TPS AU code. Note that you have to add 8 TPS master keys to your
+ keyfile. Update seems to work only on transponder 10873, e.g. switch to
+ Equidia.
+- Fixed FF concurrent streams if sc.ConcurrentFF is set to a value >1.
+- Fixed CI adapter TPDU length decoding.
+- Fixed processing stale ECM packets after a channel switch.
+- Fixed long standing bug in Nagra1 RSA key update (which was a problem in a
+ key base class, introduced in 0.5.10).
+- Fixed paged long indirect and paged long indirect indexed adressing modes in
+ Nagra cpu emu (HILOS macro).
+- Fixed cardclient reconnecting on read timeout.
+- Fixed clobbering the name of the sc shared library in the shared objects
+ table.
+- Fixed compiling if openssl lacks IDEA support.
+
+17.02.2007: Version 0.7.0
+- Forked development branch.
+- This is an alpha release. Using it in a production enviroment is not
+ recommended. You are strongly advised to read the file README.0.7.x.
+- Requires VDR version 1.5.0 or newer and openssl package 0.9.7 or newer.
+- Now operates without any patches to the VDR core.
+- Integrated FFdecsa functionality.
+- Using a openssl package without IDEA & AES support is deprecated. Included
+ support code will be removed in the future.
+- Added commandline option to force budget mode on a DVB device.
+- Added setup menu item to flush ECM cache (from ca.cache).
+- Added support for Nagra2 3DES encrypted key updates. Tweaked EMM caid and RSA
+ key selection for D+ AU.
+- Added fix for Nagra2 BEV B1 updates.
+- Added pre-crypted camkey challenges for Irdeto ACS 384. This is a last resort
+ convenience mode and usage is strongly deprecated! Don't relay on this!
+- Added NewCS client identification in cardclient newcamd.
+- Fixed include path order.
+- Added swedish translations.
+
+------------------------------------------------------------
+
+06.04.2007: Version 0.6.2
+- Backported from 0.7.2:
+ * Mismatched Lock/Unlock in smartcard code.
+ * Autodetection of pid with TPS AU data.
+ * Finnish translations.
+- Backported from 0.7.1:
+ * Nagra2 provider 0101 Map57 call.
+ * Nagra1 RSA key update.
+ * Paged adressing modes in Nagra cpu emu.
+ * TPS AU.
+ * Cardclient reconnecting on read timeout.
+ * Processing stale ECM packets after a channel switch.
+ * Clobbering shared library name in shared objects table.
+ * Compiling if openssl lacks IDEA support.
+
+17.02.2007: Version 0.6.1
+- Backported from 0.7.0:
+ * Nagra2 3DES encrypted key updates (D+ AU).
+ * Nagra2 BEV B1 updates.
+ * Pre-crypted camkey challenges for Irdeto ACS 384.
+ * NewCS client identification in cardclient newcamd.
+ * Include path order.
+ * Swedish translations.
+
+13.01.2007: Version 0.6.0
+- Stable release. Minimum supported VDR version is 1.4.0. Note that the stable
+ branch doesn't support and probably will not support VDR 1.5.x series. This is
+ left to the upcomming unstable branch.
+- Added support for new TPS algo. You need a current tps.bin (mostly changing
+ daily) file in plugins/viaccess/.
+- Fixed CW swap for Nagra2 providers 0501/1101/1102.
+- Fixed BEV inadvertently using Nagra1 ECM decoding only.
+- Fixed Nagra1 ROM10 updates (broken by ROM3 changes in 0.5.12).
+- Fixed compiling issue with missing "asm/unaligned.h".
+- Added Makefile option for static build (sasc-ng).
+
+------------------------------------------------------------
+
+15.12.2006: Version 0.5.12
+- Major restructure of Nagra code. Added basic ST19 features to Nagra cpu
+ emulation (Nano B1 processing). Added provider 0101/0901 map 3b. Fixed long
+ standing bug in ROM3 key update.
+- Updated Viaccess AU to new parsing code. Fixed signature check in shared
+ updates (introduced in 0.5.11).
+- Changed the order in which ECM pids/systems are tried to a more consistent way
+ regarding the system priority.
+- Fixed cardclient newcamd using wrong SA for Seca provider.
+- Fixed ignoring CAIDs when used together with a hardware CAM.
+- Fixed writing garbage to setup.conf if CAID ignore list is empty.
+- Fixed AutoUpdate switch. Turning it off had no effect. ExternalAU honors the
+ AutoUpdate switch now too.
+- Added russian translations.
+
+04.10.2006: Version 0.5.11
+- Added Nagra2 MECM handling, support for AUX server (version 0.9.3 or newer
+ only) and DN EMM hacks.
+- Added smartcard Cryptoworks camcrypt. You must have a valid IPK or UCPK in
+ your smartcard.conf to make it actually work. If you have a PIN for your card
+ in smartcard.conf, parental rating can be disabled even if the PIN cannot be
+ read from the card. See example smartcard.conf for format.
+- Added additional checks to Viaccess AU code to prevent segfault on bad input
+ data (e.g. short EMM).
+- Added V4 server capability check in cardclient radegast. EMM processing would
+ be possible, if the server would send UA/SA to the client.
+- Added cardclient gbox. GBOX must be running on the local machine and you have
+ to make sure that there is no /var/tmp/pmt.tmp file. Based on morfsta's
+ version but rewritten nearly from scratch.
+- Added a configurable list of CAIDs which are ignore by the plugin. See plugin
+ setup menu.
+- Fixed triggering external AU too often.
+- Fixed stupid Viacess key length error.
+- Fixed parsing CA descriptors for all SIDs if a handler has attached several.
+ Thanks to Aroureos for testing.
+
+21.07.2006: Version 0.5.10
+- Added external key updates via shell script. See README for details.
+- Now checking for duplicate serial ports in smartcard config.
+- Fixed supersede for Viaccess TPS keys.
+- Fixed re-adding DEFAULT_PORT on config-file reload.
+- Fixed design bug in key comparison.
+
+05.06.2006: Version 0.5.9
+- Smartcard code now supports setting the cardreader clock (see commandline
+ switch -C/--clock), custom baudrates (if your UART supports that), PTS
+ specific mode and PTS 1-stopbit mode. Note: due to the new clock parameter,
+ the format of DEFAULT_PORT has changed!
+- Added a filter for unused chid's in Irdeto ECM stream.
+- Fixed cardclient camd33 checking for unsuccessfull ECM answer (causing 5s
+ delay). Thanks to Aroureos for all the testing.
+- Fixed cardclient camd35 parsing provider information in EMM request.
+- Fixed a side effect in main cam loop.
+- Various fixes to Makefile (unsupported cp options, sublibrary naming scheme,
+ honour DVBDIR in Makefile.system, libcrypto linkage).
+- Fixed libvdr-sc overloading a global VDR symbol (translations).
+- Fixed compiling with VDR < 1.3.47.
+- Corrected information about min. required VDR version (currently 1.3.31).
+
+26.05.2006: Version 0.5.8
+- Completely new build system. Now using shared libraries for individual
+ encryption systems, which are loaded at runtime. Copy all wanted libsc-* to
+ your VDR plugin lib directory. Encryption systems might be provided in binary
+ form only. See README for details.
+- Added Cryptoworks emulation. See example keyfile for key format.
+- Added Nagra2 EMM nano E0 (DN) handling.
+- Don't try failed ECMs too often (by caching them).
+- Fixed Nagra2 PW inverse CW.
+- Fixed handling of channels which share the same PIDs.
+- Fixed ConstCW key handling (broken in 0.5.7).
+- Fixed Cardclient ECM caching.
+- Fixed Newcamd EMM Viaccess provider ID matching.
+- Fixed Viaccess EMM assembling.
+- Fixed dvb-cwidx patch for undefined LINUX_VERSION_CODE. There seems to be
+ compatibility problems with certain kernel/driver version (e.g. kernel driver
+ vs HG): gcc complains mutex_lock() is called with incompatible pointer type.
+ Therefore the old dvb-cwidx patch has been re-added as dvb-cwidx-old in case
+ someone has problems with the new version.
+- Fixed odd compiler error for a struct member with the same name as the struct
+ itself (probably some ancient gcc version).
+- Added frensh translations.
+
+06.05.2006: Version 0.5.7
+- Disable keyfile stuff if no system uses keys, prevent loader error messages
+ about unknown sections, prevent ca.cache trashing, fixed key saving.
+- If smartcard PTS request fails, reset card again and continue without PTS.
+- Now reading entitlements from Conax smartcard.
+- Added a workaround for providers which seem to broadcast fake ECM.
+- Added warning about channels which share the same PID.
+- Fixed Nagra2 crash with EMM-S keys.
+- Fixed compile problem when disabling certain modules.
+- Updated dvb-cwidx patch for kernel >= 2.6.16.
+- Changed Makefile to take APIVERSION into account.
+- Added polish translations.
+
+30.03.2006: Version 0.5.6
+- Added Nagra2 ECM DES decryption code.
+- Nagra signature check is now skipped, if your keyfile lacks verify keys. Don't
+ put fake verify keys in there!
+- Now reading entitlements from Cryptoworks smartcard.
+- Smartcard engine now supports ISO7816 PTS protocol i.e. baudrate changes.
+- Improved OSD display of smartcard information/entitlements.
+- Fixed exit condition in Nagra2 ECM/EMM nano parse loop.
+- Prevent frequent relaunch of the logger thread.
+- Fixed CW-index allocation for idle ECM handler.
+- Fixed missing initialisation in ECM handler.
+- Fixed ECM handler selection in special cases where a recording starts and
+ used-FF-streams == allowed-FF-streams but one of the stream is used for live
+ viewing. Involves changes to vdr-sc patch too.
+- Fixed concurrent check in vdr-sc patch. A bad comparison broke the check if
+ the first CAID in channel entry was 0x100.
+- Fixed cardclient radegast to handle NULL-cw.
+- Added dutch translations and updated finnish translations.
+- NOTE: you have to upgrade the VDR core with the supplied vdr-sc patch. Older
+ versions will refuse to work.
+
+16.02.2006: Version 0.5.5
+- Complete restructure of Nagra code (e.g. separating Nagra1 & Nagra2). Added
+ signature check, verify keys are mandatory now.
+ As a consequence Nagra2 key format has changed (as announced). See example
+ keyfile. A quick upgrade guide:
+ N xxxx 10 -> N xxxx 01
+ N xxxx 02 -> N xxxx NN 02
+ N xxxx N2 -> N xxxx NN 12
+ verify keys -> N xxxx V
+ -> N xxxx NN 03
+- Restructured AutoUpdate code. EMM data is now passed to all systems which can
+ handle it, concurrent recordings doesn't cause multiple processing of the same
+ date and it's possible to log on all available CAIDs concurrently (though this
+ may cause high CPU load). See plugin setup menu. Option LoggerActive has been
+ renamed to AutoUpdate. Option LoggerTimeout has been removed. Check your
+ config.
+- Added some statistics about EMM packet load.
+- Now using non-RSA (i.e. plain) camkey challenge for Irdeto smartcards if the
+ word PLAIN is given in smartcard.conf instead of a certificate. See example
+ smartcard.conf. For ACS 0383/0384 cards the challenge type is autodetected. If
+ anybody knows a generic way to detect the challenge type, please let us know.
+- Fixed several crypto classes to be reentrant (fixing possible race in
+ multi-threaded ECM/EMM handling).
+- Fixed access to (possibly) deleted filter class in section filter handling.
+- Fixed complaining about erroneous entries in smartcard.conf if compiled
+ without SC_IRDETO.
+- Fixed endless loop in SimpleList handling.
+- Fixed vdr-1.3.38-sc patch (one hunk was lost).
+
+08.01.2006: Version 0.5.4
+- Added a plugin SVDR interface. The only command is RELOAD for now (triggers a
+ reload of the configuration files).
+- Added support for multiple TPS keys (key is selected based on Viaccess hash).
+- Added premiliary support for Nagra2 AU. See example keyfile for format of
+ needed keys. Key format is subject to change in next releases.
+- Complete rewrite of the Seca2 provider specific code.
+- Added generic RSA & IDEA crypto classes.
+- Added better checking of CW decryption status in smartcard Cryptoworks.
+- General code review (eliminated duplicate code, replaced VDR base classes by
+ own shorter ones, beautified code).
+- Now generating smartcard Irdeto info string for OSD card menu.
+- If the DVB driver doesn't support the CA_SET_PID ioctl call (i.e. unpatched
+ driver), auto adjust the FF concurrent limit to 1.
+- Fixed checking card status in smartcard Irdeto camkey exchange.
+- Fixed detaching a PID from a handler if VDR has removed this PID from the
+ channels.conf entry meanwhile.
+- Fixed ECM table handling in ca.cache.
+- Fixed Nagra2 CW order for D+.
+- Fixed a gcc4.1 compiling issue.
+- Added additional debug output in cardclient camd35 to snoop for AU problems.
+ You may comment DEBUG_EXTRA to disable verbose log.
+- Updated FFdecsa patch to 0.1.3 (fixing a race condition).
+- Note: this release was forced by the release of VDR 1.3.38 and not all of the
+ changes above are tested as well as they should be.
+
+09.12.2005: Version 0.5.3
+- Added Nagra2 EMM support and EMM caching to cardclient newcamd.
+- Added Cryptoworks EMM support and EMM caching to cardclient camd35.
+- You may now add multiple CAID fields to cardclient.conf. A valid line would be
+ e.g. camd35:192.168.0.1:20248:1/1702/FF00,0604,0d0c/FF00:hero:itsme
+- Added support for systems which broadcast ECM on tables other than 0x80/0x81.
+- Added support for Nagra2 BEV & dual IDEA opkeys (00/10).
+- Now reading CAID from Cryptoworks smartcard.
+- Added sanity check to nano sorting to prevent memory trashing.
+- Added new CAID nano to cardclient Radegast ECM request.
+- Fixed Cryptoworks shared EMM parsing. Added additional checks to reduced EMM
+ load too.
+- Fixed smartcard Irdeto camkey challenge for Premiere S01 and other non-Z
+ cards. As these cards don't support the RSA challenge, you don't need a
+ certificate for them.
+- Fixed matching Irdeto certificates which are given with ACS version only.
+- Fixed misleading error messages in smartcard.conf parser.
+- Fixed ECM-EMM caid mismatch and segfault due to leftover debug statements in
+ cardclient camd35.
+- Fixed (i.e. removed) global cardclient EMM cache as it corrupts EMM merging
+ for systems which broadcast shared updates on different tables.
+- Fixed cardclient newcamd segfault on bad config data.
+
+12.11.2005: Version 0.5.2
+- Introducing a new config file for smartcard specific data e.g. RSA certificate
+ or box keys. The file is called smartcard.conf. See the example file.
+- Added support for full camkey challenge in smartcard Irdeto. You need a RSA
+ certificate matching your card (either Irdeto default or card specific one) in
+ smartcard.conf or your card won't work!
+- Added an abstract layer to parse and assemble EMM messages. This touches
+ several systems e.g. Viaccess, Cryptoworks & NDS. A bug here may break them
+ all.
+- Added Cryptoworks & Viaccess EMM handling to newcamd cardclient (via new
+ assemble code).
+- Added some more MAP math calls to Nagra emulation to fix Rom10 AU.
+- Several fixes to NDS EMM parsing/assembling.
+- Fixed ConstCW key lookup (was broken due to Nagra1/2 key handling changes).
+- Fixed cardclient camd3 to include SID in ECM request.
+- Fixed cardclient newcamd to handle NULL-cw which are send by newcs.
+- Removed logger option 'non-recording on' from the config. In 0.5.x it is
+ without function anyways.
+- Added a message to hint the user about unusual values in ScCaps. Changed the
+ default values to something more reasonable too.
+- Updated FFdecsa patch to 0.1.2 (fixing a segfault).
+
+13.10.2005: Version 0.5.1
+- Added smartcard Cryptoworks EMM handling. There is a setup option to disable
+ the parental rating on the card too (experimental). Thanks to scotty for the
+ sample code.
+- Added smartcard Viaccess EMM handling (experimental).
+- Added FFdecsa-0.1.1 patch.
+- Don't count a live stream on a FF card when deciding about allowed
+ concurrency. A live stream doesn't use bandwidth on the bus.
+- Apply min. ECM processing time to Nagra2 too.
+- Fixed segfault for Seca1.
+- Fixed Irdeto key updates. Somehow/sometime the code was broken.
+- Fixed cardclient newcamd NDS EMM (still experimental).
+- Now considering the key size when superseding keys.
+- Forgot to mention that you need additional table files for Seca 6a (s2_sse.bin
+ 5120; s2_sse_006a.bin 336; s2_cw_006a.bin 512 (Numbers are the filesize)).
+- Updated finnish translations.
+
+15.09.2005: Version 0.5.0
+- Forked development branch.
+- Added concurrent recording feature i.e. record/view multiple encrypted
+ channels on a single DVB card. This also works on a full-featured DVB card!
+ To make this feature work you NEED:
+ on a BUDGET card:
+ * SoftCSA version 0.1.0 or greater
+ on a FULL-FEATURED card:
+ * a DVB driver patched with dvb-cwidx.diff
+ * a specialy patched firmware. The "normal" patched firmware doesn't work!
+ See further details in the README.
+- vdr-sc patch has been updated. This plugin version ONLY works with this patch!
+ Older plugin versions doesn't work with this patch, although they may compile
+ fine!
+- Dropped compatibility for older VDR versions. Requires VDR version 1.3.29 or
+ newer.
+- Changed card & provider handling in cardclient camd35 (experimental).
+- Added NDS support to newcamd cardclient (experimental).
+- Added limited Seca 6a support.
+- Fixed mismatching Nagra1 & Nagra2 keys.
+
+------------------------------------------------------------
+
+17.12.2005: Version 0.4.12
+- Backported from 0.5.3:
+ * Fixed cardclient newcamd segfault on bad config data.
+ * Fixed ECM-EMM caid mismatch and segfault in cardclient camd35.
+ * Added new CAID nano to cardclient Radegast ECM request.
+- Backported from 0.5.2:
+ * Fixed ConstCW key lookup.
+ * Fixed cardclient camd3 to include SID in ECM request.
+ * Fixed cardclient newcamd to handle NULL-cw.
+ * Changed plugin default conf values to something more reasonable.
+- This is the final release for the 0.4.x branch. The 0.5.x branch is already
+ pretty stable. Please consider upgrading.
+
+31.10.2005: Version 0.4.11
+- Backported from 0.5.1:
+ * Fixed segfault for Seca1.
+ * Fixed Irdeto key updates.
+ * Now considering the key size when superseding keys.
+ * Updated finnish translations.
+
+24.09.2005: Version 0.4.10
+- Apply min. ECM processing time to Nagra2 too.
+- Backported from 0.5.0:
+ * Fixed mismatching Nagra1 & Nagra2 keys.
+ * Limited Seca 6a support.
+
+10.09.2005: Version 0.4.9
+- Fixed cardclient camd35 to include provider ID in ECM request.
+- Fixed Nagra2 padding 2nd RSA and CW swap for NA.
+- Fixed compiling issue in Nagra code on older VDR versions.
+- Fixed compiling issue in IDEA code if a recent openssl but without IDEA is
+ installed.
+
+04.09.2005: Version 0.4.8
+- Added Nagra2 code. See example keyfile for key format.
+- Fixed coredump in ECM delay feature.
+- Fixed buffer overflow in cardclient camd35.
+- Fixed setting serial IO speed for smartcard access.
+- Now truncating all key printouts in the debug log.
+
+18.08.2005: Version 0.4.7
+- Fixed compatibility issue with camd 3.7x.
+- Fixed switching to FTA while card is replaying.
+- Changed decoding, so that if the decoding fails consecutively, further
+ processing is delayed until the next parity change.
+- Fixed ECM card response parsing in smartcard Conax.
+- Added some generic ECM/EMM parsing.
+- Updated finnish translations.
+
+20.06.2005: Version 0.4.6
+- Added EMM support for smartcard Conax.
+- Added EMM support to Newcamd client.
+- Fixed EMM card command in smartcard Seca.
+- Fixed Camd Cmd05/Cmd06 offsets.
+- Now really fixed canceling netwatcher thread.
+
+25.05.2005: Version 0.4.5
+- Fixed Nagra key update issues.
+- Added support for the new Camd Cmd05/Cmd06. Thanks to Dukat for the example.
+- Added support for broadcasts without ECM data (only if system delivers
+ constant CW). Based on Ragnos patch.
+- Fixed ca.cache handling if ECM PID changed since entry was created.
+- Fixed Radegast client to work with shared Seca cards.
+- Fixed netwatcher cancel timeout.
+- Now using unaligned.h in cDes to prevent unaligned memory accesses on
+ architectures which doesn't support them (e.g. Alpha).
+
+07.02.2005: Version 0.4.4
+- Now caching CA descriptor data to speed up intial sync for systems which
+ depend on this data (e.g. Seca).
+- Fixed Nagra EMM code to handle additional nanos in front of cpu updates.
+- Fixed Seca RSA for short decrypt results.
+- Fixed Viaccess EMM assemble buffer size.
+
+21.01.2005: Version 0.4.3
+- Added Seca2 nano 5109 handling. You need special RSA keys for this. See
+ example keyfile.
+- Increased wait timeout for ECM extra data.
+- Fixed Seca permtables. Thanks to millemila.
+- Fixed Seca SHA1 signature. Thanks to BB.
+- Fixed return length position in Newcamd cardclient. Thanks to cart.
+
+10.01.2005: Version 0.4.2
+- Added workarounds for changes in vdr 1.3.18.
+- Fixed comparision of BIGNUM keys (Nagra, but not only Nagra).
+- Fixed cCondWait calls for vdr before 1.3.13.
+
+23.12.2004: Version 0.4.1
+- Added constant-CW system, CONSTCW=1 to activate. See example keyfile for CW
+ key format.
+- Added support for Viaccess TPS crypt v2. You need a suitable TPS key for your
+ provider. See example keyfile.
+- Added support for v5.25 protocol in Newcamd cardclient (with fallback to old
+ protocol).
+- Added support for Nagra RSA key updates (high system id only). You need a
+ proper eeprom file for this to work.
+- Now delaying first access to smartcards until the cards are initialised.
+- Fixed Seca code which though it has a correct decode while it hasn't.
+- Fixed several minor issues in Nagra code.
+- Fixed CA descriptor parsing for sc-viaccess and cardclients.
+- Fixed cardd client to send network message of at least 96 bytes.
+- Fixed inline asm macros to be used only on x86 CPUs.
+- Replaced non-reentrant libc functions with their reentrant counter part.
+- Replaced usleep() calls with proper cCondWait calls (vdr 1.3.x only).
+
+26.10.2004: Version 0.4.0
+- Versions bump, now in stable branch.
+- Added Viaccess smartcard code, SC_VIACCESS=1 to activate. ECM only for now.
+- Added ragnos Sc-Seca-PPV patch.
+- Added zens FullX-2 patch.
+- Added permtable for Seca provider 0x65.
+- Added cardclient config option to trigger immediate connect to cardserver on
+ startup.
+- Extended smartcard code to support indirect convention, zero-byte writes and
+ 256-byte reads.
+- Fixed Nagra plaintext AU.
+- Fixed camd33 reconnecting and discard keep-alive packets.
+- Removed unnecessary bind() in udp networking (allows camd35 server on same
+ machine).
+- Fixed disconnect timeout not working if no dial script is given.
+- Updated finnish translations.
+
+------------------------------------------------------------
+
+26.09.2004: Version 0.3.17
+- Joined "dukat" & "freezer" cardclients to new "camd33" client.
+- Added cardclient "camd35", supporting camd 3.5 udp protocol.
+- Added caid & mask to cardclient config. Added carddata (hexbase, hexser) to
+ Aroureos cardclient config. See example cardclient.conf.
+- Fixed Viaccess logger (wrong section filter mask).
+- Fixed dialup networking not hanging up in case of connect error.
+
+14.09.2004: Version 0.3.16
+- Now supporting all Nagra keysets (pk0-2,typ0-1). Keyformat has changed. See
+ example keyfile.
+- Fixed some long standing bugs in Nagra ECM handling. Verify keys (V) are
+ mandatory now, 80 keynumber is obsolete. Note: you need both keysets for a
+ provider, e.g. for Polsat 7001 & 7101.
+- Added support for Seca provider 0x65 (appropriate hash/mt files needed).
+- Added finnish translations.
+- Fixed key length mismatch on EMM update for 8/16 byte keys.
+- Fixed sc-cryptoworks ATR parsing for cards with bios3mod.
+- Fixed stupid typo bug in sc-Irdeto which prevented EMM updates at all
+ (introduced in .13), fixed potentional buffer overflows.
+- Fixed Seca to really loop through all available keys (with same key nr).
+- Several compatibility fixes for 64-bit systems.
+
+07.08.2004: Version 0.3.15
+- Unified Seca, Viaccess & Nagra DES implementation.
+- Added CAID parsing to dukat cardclient.
+- Added support for 16-byte Seca keys.
+- Added Seca2 0064 51 nano processing.
+- Attempt to fix cardclient EMM update problem (appearently AES related).
+- Fixed Nagra CPU emu (random generator).
+
+23.07.2004: Version 0.3.14
+- Fixed Seca2 nano processing (Thanks to Sandali for new permutation values).
+- Fixed missing CAT parsing in Viaccess logger.
+- Fixed logger debug output (cardNum vs. CardNum()).
+- Fixed error-case memory leak in Nagra file mapper.
+
+01.07.2004: Version 0.3.13
+- Added Viaccess2 algo. Note that you need 16-byte keys for Viaccess2.
+- Added extended Seca2 nano handling.
+- Added provider based addressing and Irdeto2 support in common cardclient.
+- Added local caching of ECM/EMM messages to reduce cardserver load.
+- Major code restructure to unify system & logger code.
+- Fixed reconnecting to server in newcamd client.
+- Some changes for gcc 3.4.x compatibility.
+
+06.06.2004: Version 0.3.12
+- Added new Seca2 nano handling. Thanks to Nightshad.
+- Added some glue for VDR 1.3.7+ compatibility.
+- Major code rewrite in cardclient. Use CARDCLIENT=1 to activate. Configuration
+ of cardclients has changed completely. See README.
+- New network code supporting on demand dialup networking.
+- Added Radegast and Newcamd client.
+- Added minimum processing time to Nagra ECM code (emulates card processing time
+ in case timing depends on this). Configurable from the setup menu (0=disabled).
+- Fixed Nagra EMM tester code for DISH and ROM10/11 OTP size.
+- Fixed some possible table overflows in Seca code.
+- Fixed processing of short blocks in Conax code.
+- Fixed FTA switch in case VDR shortly interrupts a recording (e.g. when
+ changing PIDs).
+
+07.04.2004: Version 0.3.11a
+- Fixed Seca2 segfault on mask table access.
+- Now flushing ECM filter buffers on error.
+
+02.04.2004: Version 0.3.11
+- Added Seca2 support. Openssl and mask/hash table files needed. See README.
+ Seca1 compatiblility not yet tested. Thanks to Nightshad.
+- Added fix for Nagra ROM11 and plain key updates.
+- Using multiple threads to read from PID filters. Prevents that high-latency
+ systems (like smartcards) block out other systems.
+- Remember the system name in ca.cache and use this system first later on.
+ Prevents delays for low-priority systems if ECM is cached.
+- Fixed smartcard Seca PBM checking for cards which doesn't support the needed
+ card command.
+- Fixed network timeout and flushing read buffer in cardserver client.
+- Fixed transponder handling for VDR 1.3.x (frequency vs. transponder).
+
+22.02.2004: Version 0.3.10
+- Added cardserver client (Streamboard client). EMM transfers are disabled at
+ the moment. Contributed by S.Laurel.
+- Added support for Conax smartcards. Thanks to Nightshad.
+- Added provider based addressing for EMM updates in Irdeto smartcard.
+- Added commandline option to support card reader with reverse reset line (-R).
+- Added make option to add a smartcard default port (DEFAULT_PORT).
+- Reworked plugin setup menu. Added separate status page. Added smartcard
+ information page (press OK on the smartcard interface line).
+- Added code to allow system specific setup options.
+- Fixed off by one error and more mixed up arguments in Seca1 decryption.
+- Fixed typos in Viaccess logger debug statements.
+
+05.02.2004: Version 0.3.9
+- Fixed memory initialisation in Nagra cardemu.
+- Fixed filedescriptor leak in Nagra ROM mapping code.
+- Fixed MECM XOR table if there are less than 64 bytes. Thanks to Vlinders.
+- Fixed off by one error in (unused) smartcard Irdeto camcrypt code.
+- Fixed mixed up function arguments in Seca1 decryption.
+- Fixed Viaccess logger for ViaSat and TPS shared updates. Note that the format
+ of the Viaccess.KID file has changed (SA added). Thanks to Nightshad.
+- Changed default make options. Irdeto, Seca & Viaccess systems aren't compiled
+ by default anymore. Use IRDETO=1 SECA=1 VIACCESS=1 for old behaviour.
+
+18.01.2004: Version 0.3.8
+- Several improvements to the Nagra code (Cardemu, MECM). Completely new
+ layout, no more libnagra. Now you need some Eeprom files and the location for
+ the ROM files has changed. See README file.
+ Thanks to Nightshad, BB and their friends.
+- Fixed Irdeto smartcard EMM command creation for PW. Also don't overrun the
+ card with ECM requests in case of not subscribed/expired channel.
+- Reworked Seca, Viaccess and Irdeto crypto code. No more libmgcam.
+- Added commandline option to detect smartcards in readers with reverse CD.
+- Added a SC patch for vdr 1.3.1 and some glue to the plugin. See README for
+ notes about 1.3.1 support.
+
+13.12.2003: Version 0.3.7
+- Several Nagra auto-update fixes. Thanks to Nightshad.
+- Fixed Irdeto smartcard EMM path. Thanks to Scotty.
+- Fixed wrong behaviour in case of transfer mode.
+- Added support to load Nagra ROM extentions (needs ROM?ext.bin files).
+- Now checking PBM and date in Seca smartcard. Fixed card status for EMM
+ updates. Thanks to Nightshad.
+- Added a VDR patch to prevent SC activation during EPG scans.
+ Contributed by Andy.
+- Changed CICAM handling. SC activation is now more channel base.
+ Note: you have to update your config! See README file.
+
+8.11.2003: Version 0.3.6
+- Added support for Irdeto smartcards (e.g. Premiere). Thanks to Scotty.
+- Added smartcard insert/remove detection, card auto-initialisation and support
+ for multiple card interfaces.
+- Enhanced smartcard ISO functions (e.g. 0x60 handling, ATR parsing).
+- Fixed smartcard Cryptoworks (V3 ATR, serial timeout).
+- Fixed Nagra ROM10 emu (Cabo 4801).
+- Fixed saving ECM values to ca.cache.
+- Fixed removing of cached ECM entries so that only really failed entries are
+ removed.
+
+24.10.2003: Version 0.3.5
+- Fixed Nagra cardemulator (MUL bug, OTP area, etc). Thanks to BB & Nightshad.
+- Added handling for ROM specific Nagra keys. See examples/Softcam.Key.
+- Fixed display of used Nagra key in the OSD.
+
+18.10.2003: Version 0.3.4
+- Added EMM logger in sc-Seca to auto-update card data. Works for Seca2 cards
+ too. Thanks to Nightshad.
+- Added cardemulator to execute ROM code for improved Nagra key updates. Thanks
+ to BB & Nightshad. Probably there are still some issues with providers which
+ require seperate keys for EMM processing. This will be addressed in the next
+ release.
+- Now removing old, failed ECM entries from the cache.
+- Fixed possible race condition in Nagra rom locking.
+- Fixed memory leaks in Nagra & Conax code (BIGNUM handling).
+
+18.07.2003: Version 0.3.3
+- Allow new keys to supersede/invalidate older keys (only for systems with
+ unique key identifiers; for now all beside Irdeto & @SHL). This should fix
+ Viaccess key updates.
+- Made Viaccess loop through all available keys while decrypting.
+
+05.07.2003: Version 0.3.2
+- Added some information about @SHL to the README.
+- Cleaned up @SHL code. Now supporting multiple keys in keyfile, no need to
+ uncomment unused keys.
+- New code for hex dumping of incoming data (see common.h for activation).
+- Excluding a range of potentional "fake" ECM pids for Viaccess system. Does
+ this affects any valid channel?
+
+24.06.2003: Version 0.3.1unstable
+- Completely new section filter handling.
+- Restructure of the logger code (will allow EMM processing for smartcards).
+- Reworked Viaccess logger. Thanks to BB & Nightshad.
+- Added @SHL (SkyCrypyt) support. Thanks to Nightshad and his friends.
+- Fixed writing duplicate ecm entries into cache file.
+- Fixed SoftCSA activation (was broken in 0.3.0, 0.2.x is fine).
+- Fixed saving Viaccess keys with odd digit count.
+- Fixed leaving a locked mutex in CAM setup.
+- Fixed crash in smartcard setup if no serial device was given.
+- Added examples files to show file formats.
+- Added system specific Makefile's (*.mk).
+- Added french translations.
+
+25.05.2003: Version 0.3.0unstable
+- Forked 0.3.x unstable branch.
+- Major restructure of CA system code.
+- Added Viaccess logger. Requires Viaccess.KID. Thanks to BB for providing
+ the sample code.
+- Generalized smartcard code. Use new plugin commandline option -s to set
+ serial port. Use setup menu to select card type.
+- Now supporting Seca smartcards. Another big thanks to Nightshad.
+- Reworked Cryptoworks smartcard code. Please report working status.
+
+------------------------------------------------------------
+
+29.05.2003: Version 0.2.2
+- Added patch for vdr 1.1.32/33.
+- Added french translations. Thanks to Piout.
+
+14.05.2003: Version 0.2.1
+- Adapted to the changes in (and now also requires) VDR 1.1.31.
+
+05.05.2003: Version 0.2.0
+- Some code cleanup.
+- Fixed Nagra key saving.
+- Added Greek translations. Thanks to Aroureos.
+- Stable release version. A new development branch will fork soon.
+
+27.04.2003: Version 0.2.0rc1
+- Added patch for vdr 1.1.29
+
+24.04.2003: Version 0.1.13
+- Rescanning CaDescr from time to time in case that we initialy got some
+ descriptors but are unable to find a valid key with them.
+- Some more Cryptoworks fixes.
+- Fixed BN bug in Nagra RSA code.
+- Newbie protection: actively checking required VDR version and some patch
+ versions.
+- Adapted to the changes in (and now also requires) VDR 1.1.28. Timeshifting of
+ encrypted channels works. You must use a patched LL firmware for this feature,
+ the -icam firmware won't allow timeshift.
+
+14.04.2003: Version 0.1.12
+- Changed debug messages to avoid confusion between loaded keys and loaded
+ cards *sigh*.
+- Now using VDR internal functions to get the ECM pids. This requires VDR 1.1.27
+ to work. Note also, that there is a new sc patch for VDR 1.1.27.
+- Make the ECM cache take care of the fact that the SID may not be unique (now
+ matching for source & transponder too).
+- Cleanup of Nagra code (now in libnagra). Got rid of the miracle stuff, now
+ using libcrypto (openssl) here too. Big thanks to Nightshad.
+- Added (untested) Nagra BEV/DISH support.
+
+21.03.2003: Version 0.1.11
+- Fixed segmentation fault on plugin exit (stupid: static initialization of
+ list members is a no-no).
+- Increased timeout for reading PAT.
+- Some fixes to the Cryptoworks code. Hope this works better. No testing here.
+
+03.03.2003: Version 0.1.10
+- Improved "stop logger" with transfer mode. Disabled this feature for budget
+ cards. They don't crash with logger enabled.
+- Fixed stopping sc processing if a transfer mode ends.
+- SoftCSA now available as separate archive.
+
+19.03.2003: Version 0.1.9
+- Fixed Nagra MECM handling.
+- Logger is stopped when a transfer mode is detected, too.
+- Experimental SoftCSA support. See README for setup instructions. Thanks to
+ emumensch for the initial version.
+
+31.01.2003: Version 0.1.8
+- Fixed Conax decoding and key handling.
+- Fixed Nagra key strings which havn't been NULL terminated.
+- Fixed loading SoftCam.Key with Nagra/Conax keys if one or both have been
+ disabled at compile time.
+- Fixed long standing bug in FastKey().
+- Added Nagra logger. No need for a Nagra.KID, the keys should still be in
+ SoftCam.Key if you are able to watch Nagra channels. Thanks to Nightshad.
+
+15.01.2003: Version 0.1.7
+- Switched to Conax code from mgcam_20030114 (much shorter). You must have
+ installed libcrypto (part of openssl). This fixes the Conax linking problem in
+ 0.1.6 too.
+- Nagra & Conax keys are now loaded from SoftCam.Key too. See README for format.
+
+14.01.2003: Version 0.1.6
+- Fixed Viacess code for NTV and TPS crypt (mgcam_20021123).
+- Extended setup option for logger. Logger can be stopped during recordings as
+ soon as a valid key is found. Is this more stable?
+- Fixed a compile error in Nagra code (miracl.h).
+- Switched Cryptoworks support to libcwemu. Serial line detection is now in the
+ main Makefile, see ISO_TTY. CRYTOWORKS=1 to enable.
+- Added Conax support, based on lincardemu0.1.9n and some adaptions to mgcam
+ found on the net. Thanks to all the guys for their work. CONAX=1 to enable.
+- Fixed PMK update in Irdeto logger.
+- Updated card data is also written to ca.cache now. The ca.cache format has
+ changed for this. The old format is converted automatically.
+- Changed detection of fake channel id's.
+- Improved filter settings and paket reading.
+
+16.11.2002: Version 0.1.5
+- Adapted to the changes in vdr 1.1.15. Now requires vdr >=1.1.15 and HEAD
+ driver.
+- Added Cryptoworks support together with original cards and a cardreader with
+ Phoenix ISO interface at the serial line. Add CRYPTOWORKS=1 to enable. (see
+ libmgcam/cryptoworks.c for serial line selection).
+- Remove xor/firmware hacks (not needed anymore).
+
+28.10.2002: Version 0.1.4
+- Adapted to the changes up to vdr 1.1.14 and the new HEAD DVB driver.
+- Added option to setup menu to reload config files.
+- Added code to detect fake channel id's on primafila.
+- Minor logger fixes.
+
+28.09.2002: Version 0.1.3
+- Now compiles with vdr 1.1.9+.
+- Cleaned up Viaccess code.
+- Added Seca logger.
+- Fixed switching back to a FTA channel after recording.
+
+09.09.2002: Version 0.1.2
+- Load cardinfos/cacache even if no keyfile found.
+- Fixed rescanning of PAT/PMT if temporarily no enc. system found.
+- Fixed timeout handling while reading from dvb device.
+- Removed usleep() loops from logger code.
+
+29.08.2002: Version 0.1.1
+- Added missing code to save the xor & firmware hack setup.
+
+22.08.2002: Version 0.1.0
+- Initial release.
--- /dev/null
+#
+# Softcam plugin to VDR
+#
+# This code 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 code 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+
+# The official name of this plugin.
+# This name will be used in the '-P...' option of VDR to load the plugin.
+# By default the main source file also carries this name.
+#
+PLUGIN = sc
+
+### The version number of this plugin (taken from the main source file):
+
+VERSION = $(shell grep 'define SCVERSION' version.h | awk '{ print $$3 }' | sed -e 's/[";]//g')
+SCAPIVERS = $(shell sed -ne '/define SCAPIVERS/ s/^.[a-zA-Z ]*\([0-9]*\).*$$/\1/p' $(PLUGIN).c)
+
+### The directory environment:
+
+VDRDIR = ../../..
+LIBDIR = ../../lib
+SYSDIR = ./systems
+PREDIR = ./systems-pre
+TMPDIR = /tmp
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -O2 -g -fPIC -Wall -Woverloaded-virtual
+
+### Includes and Defines
+
+INCLUDES = -I$(VDRDIR)/include
+DEFINES = -DPLUGIN_NAME_I18N='"$(PLUGIN)"'
+SHAREDDEFINES = -DAPIVERSNUM=$(APIVERSNUM) -D_GNU_SOURCE
+LIBS = -lcrypto
+SHAREDLIBS =
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The version number of VDR (taken from VDR's "config.h"):
+
+VDRVERSION = $(shell sed -ne '/define VDRVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+APIVERSION = $(shell sed -ne '/define APIVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+ifeq ($(strip $(APIVERSION)),)
+ APIVERSION = $(VDRVERSION)
+endif
+VDRVERSNUM = $(shell sed -ne '/define VDRVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+APIVERSNUM = $(shell sed -ne '/define APIVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+ifeq ($(strip $(APIVERSNUM)),)
+ APIVERSNUM = $(VDRVERSNUM)
+endif
+
+### The name of the distribution archive:
+
+ARCHIVE = $(PLUGIN)-$(VERSION)
+PACKAGE = vdr-$(ARCHIVE)
+
+### The object files (add further files here):
+
+OBJS = $(PLUGIN).o data.o filter.o system.o i18n.o misc.o cam.o \
+ smartcard.o network.o crypto.o system-common.o parse.o log.o
+
+#
+# generic stuff
+#
+
+# smartcard default port
+ifdef DEFAULT_PORT
+ TEST := $(shell echo '$(DEFAULT_PORT)' | sed -ne '/".*",.*,.*,.*/p')
+ ifneq ($(strip $(TEST)),)
+ DEFINES += -DDEFAULT_PORT='$(DEFAULT_PORT)'
+ else
+ $(error DEFAULT_PORT has bad format)
+ endif
+endif
+
+# max number of CAIDs per slot
+MAXCAID := $(shell sed -ne '/define MAXCASYSTEMIDS/ s/^.[a-zA-Z ]*\([0-9]*\).*$$/\1/p' $(VDRDIR)/ci.c)
+ifneq ($(strip $(MAXCAID)),)
+ DEFINES += -DVDR_MAXCAID=$(MAXCAID)
+endif
+
+# FFdeCSA
+CPUOPT ?= pentium
+PARALLEL ?= PARALLEL_32_INT
+CSAFLAGS ?= -Wall -fPIC -g -O3 -mmmx -fomit-frame-pointer -fexpensive-optimizations -funroll-loops
+FFDECSADIR = FFdecsa
+FFDECSA = $(FFDECSADIR)/FFdecsa.o
+
+# export for system makefiles
+export SCAPIVERS
+export APIVERSION
+export INCLUDES
+export SHAREDDEFINES
+export SHAREDLIBS
+export CXX
+export CXXFLAGS
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(SHAREDDEFINES) $(INCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): $(OBJS:%.o=%.c) $(wildcard *.h)
+ @$(MAKEDEP) $(DEFINES) $(SHAREDDEFINES) $(INCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+ifdef STATIC
+BUILDTARGETS = $(LIBDIR)/libvdr-$(PLUGIN).a systems
+SHAREDDEFINES += -DSTATICBUILD
+else
+BUILDTARGETS = $(LIBDIR)/libvdr-$(PLUGIN).so.$(APIVERSION) systems systems-pre
+endif
+
+all: $(BUILDTARGETS)
+.PHONY: systems systems-pre clean clean-core clean-systems clean-pre dist srcdist
+
+libvdr-$(PLUGIN).so: $(OBJS) $(FFDECSA)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) $(FFDECSA) $(LIBS) $(SHAREDLIBS) -o $@
+
+$(LIBDIR)/libvdr-$(PLUGIN).so.$(APIVERSION): libvdr-$(PLUGIN).so
+ @cp -p $< $@
+
+$(LIBDIR)/libvdr-$(PLUGIN).a: $(OBJS)
+ $(AR) r $@ $(OBJS)
+
+$(FFDECSA): $(FFDECSADIR)/*.c $(FFDECSADIR)/*.h
+ @$(MAKE) COMPILER="$(CXX)" FLAGS="$(CSAFLAGS) -march=$(CPUOPT) -DPARALLEL_MODE=$(PARALLEL)" -C $(FFDECSADIR) all
+
+systems:
+ @for i in `ls -A -I ".*" $(SYSDIR)`; do $(MAKE) -f ../../Makefile.system -C "$(SYSDIR)/$$i" all || exit 1; done
+
+systems-pre:
+ @for i in `ls -A -I ".*" $(PREDIR) | grep -- '-$(SCAPIVERS).so.$(APIVERSION)$$'`; do cp -p "$(PREDIR)/$$i" "$(LIBDIR)"; done
+
+clean-systems:
+ @for i in `ls -A -I ".*" $(SYSDIR)`; do $(MAKE) -f ../../Makefile.system -C "$(SYSDIR)/$$i" clean; done
+
+clean-core:
+ @$(MAKE) -C testing clean
+ @$(MAKE) -C $(FFDECSADIR) clean
+ @-rm -f $(LIBDIR)/libsc-*-$(SCAPIVERS).so.$(APIVERSION)
+ @-rm -f $(LIBDIR)/libvdr-$(PLUGIN).a $(LIBDIR)/libsc-*.a
+ @-rm -f $(OBJS) $(DEPFILE) *.so *.tar.gz core* *~
+
+clean-pre:
+ @-find "$(PREDIR)" -type f -not -iname "*-$(SCAPIVERS).so.*" | xargs rm -f
+
+clean: clean-core clean-systems
+
+dist: clean-core
+ @for i in `ls -A -I ".*" $(SYSDIR)`; do $(MAKE) -f ../../Makefile.system -C "$(SYSDIR)/$$i" dist; done
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @path="$(TMPDIR)/$(ARCHIVE)/$(notdir $(SYSDIR))";\
+ for i in `ls -A -I ".*" $$path`; do if [ -f "$$path/$$i/nonpublic.mk" ]; then rm -rf "$$path/$$i"; fi; if [ -f "$$path/$$i/nonpublic.sh" ]; then (cd $$path/$$i ; source ./nonpublic.sh ; rm ./nonpublic.sh); fi; done
+ @strip --strip-unneeded --preserve-dates $(TMPDIR)/$(ARCHIVE)/$(notdir $(PREDIR))/*
+ @tar czf $(PACKAGE).tar.gz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Distribution package created as $(PACKAGE).tar.gz
+
+fulldist: clean clean-pre
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @mkdir $(TMPDIR)/$(ARCHIVE)
+ @cp -a * $(TMPDIR)/$(ARCHIVE)
+ @tar czf $(PACKAGE)-full.tar.gz -C $(TMPDIR) $(ARCHIVE)
+ @-rm -rf $(TMPDIR)/$(ARCHIVE)
+ @echo Full distribution package created as $(PACKAGE)-full.tar.gz
--- /dev/null
+#
+# Softcam plugin to VDR
+#
+# This code 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 code 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, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+# Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+
+### The directory environment:
+
+LIBDIR = ../../../../lib
+PREDIR = ../../systems-pre
+
+### Includes and Defines
+
+SINCLUDES = -I../..
+SINCLUDES += $(shell echo "$(INCLUDES)" | sed -e 's+-I *+-I+g' | sed -e 's+-I\([^/]\)+-I../../\1+g')
+
+-include *.mk
+
+### Implicit rules:
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(SHAREDDEFINES) $(SINCLUDES) $<
+
+# Dependencies:
+
+MAKEDEP = g++ -MM -MG
+DEPFILE = .dependencies
+$(DEPFILE): $(OBJS:%.o=%.c) $(wildcard *.h)
+ @$(MAKEDEP) $(DEFINES) $(SHAREDDEFINES) $(SINCLUDES) $(OBJS:%.o=%.c) > $@
+
+-include $(DEPFILE)
+
+### Targets:
+
+LIBSC = libsc-$(TARGET).so
+LIBSCVER = libsc-$(TARGET)-$(SCAPIVERS).so.$(APIVERSION)
+LIBSCAR = libsc-$(TARGET).a
+
+ifdef STATIC
+BUILDTARGETS = $(LIBDIR)/$(LIBSCAR)
+else
+BUILDTARGETS = $(LIBDIR)/$(LIBSCVER) $(PREDIR)/$(LIBSCVER)
+endif
+
+define clean
+@-rm -f $(OBJS) $(CLEAN_RM) $(DEPFILE) $(LIBSC) core* *~
+endef
+
+define clean-pre
+@find "$(PREDIR)" -type f -iname "libsc-$(TARGET)-*.so.*" -not -iname "libsc-$(TARGET)-$(SCAPIVERS).so.*" | xargs rm -f
+@-rm -f $(PREDIR)/$(LIBSCVER)
+endef
+
+all: $(BUILDTARGETS)
+.PHONY: clean dist
+
+$(LIBSC): $(OBJS)
+ $(CXX) $(CXXFLAGS) -shared $(OBJS) $(LIBS) $(SHAREDLIBS) -o $@
+ $(clean-pre)
+
+$(LIBDIR)/$(LIBSCVER): $(LIBSC)
+ @cp -p $< $@
+
+$(LIBDIR)/$(LIBSCAR): $(OBJS)
+ $(AR) r $@ $(OBJS)
+
+ifdef NONPUBLIC
+$(PREDIR)/$(LIBSCVER): $(LIBSC)
+ $(clean-pre)
+ @cp -p $< $@
+
+dist: $(PREDIR)/$(LIBSCVER)
+ $(clean)
+else
+$(PREDIR)/$(LIBSCVER):
+
+dist:
+ $(clean-pre)
+ $(clean)
+endif
+
+clean:
+ $(clean-pre)
+ $(clean)
--- /dev/null
+This is a "plugin" for the Video Disk Recorder (VDR).
+
+See the file COPYING for license information.
+
+Description: SoftCAM for Irdeto, Seca, Viaccess, Nagra, Conax & Cryptoworks
+
+-----------------------------------------------------------------------
+
+
+
+WARNING! This is the SC plugin development branch !!
+
+This is a beta release. We encourage everybody to try this release, nevertheless
+it should be used under controlled conditions.
+
+Feel free to put your bugreports, suggestions and/or wishes to the 4free or DVBN
+board. A bugreports should at least include the console log.
+
+
+
+What is it ?
+------------
+
+First: Most certainly it's not legal to use this software in most countries of
+the world. But probably you already know this...
+
+SC means softcam, which means a software CAM emulation.
+
+The main development goal for this branch is a full-featured implementation
+without the needs to patch VDR. Recent changes in the new VDR development branch
+opened a possibility for that.
+
+The plugin captures the DVB devices in an early startup stage of VDR startup
+(before VDR itself has got access to them) and makes VDR believe that there is
+another CAM connected to the device. This CAM is emulated from the plugin by
+providing a cut-down EN50221 interface for communication with the VDR core. From
+VDR views there is no difference between a real hardware CAM and the emulated
+CAM.
+
+The plugin decrypts the scrambling codewords from the incomming ECM stream. The
+actual descrambling of the video stream is either done by the ECD chip on
+full-featured DVB cards or with the included FFdecsa implementation on budget
+cards.
+
+This piece of software is originaly based on (and still contains code from)
+mgcam (a standalone CAM emulation). Many thanks to the (anonymous) author for
+his really fine piece of software :-)
+
+
+
+Requirements
+------------
+
+* DVB driver from dvb-kernel 2.6 or 2.4 branch with applied patches
+* a patched firmware version 2620 or newer
+* VDR 1.5.0 or newer (VDR 1.4.6+ in compatibility mode, see 1.4.x setup section)
+* Openssl package version 0.9.7 or newer
+
+
+
+How to setup ?
+--------------
+
+First you should start with a recent dvb-kernel driver (cvs recomended). Copy
+the patched firmware in place and apply at least the dvb-cwidx patch. Make sure
+that you use a patched firmware if you intend to use the plugin together with a
+full-featured DVB card. You definitely need a patched firmware in this case, but
+only recent versions support concurrent recording! Recompile the driver, unload
+the modules, install the new ones and reload the DVB driver. If you suffer from
+ARM crashes, add "hw_sections=0" while loading the dvb-ttpci module.
+
+Contrary to older plugin versions you MUST NOT apply any patches to the VDR core
+(neither vdr-sc nor ffdecsa/softcsa).
+
+You must have installed the openssl development files. For most distributions
+this means to install openssl-devel package. You should use a openssl package
+with AES and IDEA enabled, as support for openssl without these will be removed
+in the near future.
+
+Now follow the VDR instruction to compile plugins (make plugins). Beside the
+core plugin (libvdr-sc.so), the make process (if successfull) creates an
+additional shared library object for every supported system (libsc-*.so). You
+can enable/disable individual systems by adding or removing the shared library
+from your VDR plugin lib directory.
+
+Starting with version 0.5.8 all make compile time options (e.g. IRDETO=1) except
+DEFAULT_PORT have been removed!
+
+Note that in combination with other plugins which create devices (e.g.
+softdevice) it's essential that this plugin is loaded before any of these
+plugins, i.e. as a rule of thumb put this plugins first on the VDR commandline.
+The plugin will fail on startup if the plugin load order results in mismatched
+device numbering inside VDR.
+
+Note that some budget card drivers provide a CA device too. This might make VDR
+and the plugin detect the card as a full-featured card, thus disabling FFdecsa.
+You should use commandline option -B to force detection as a budget card in such
+a case.
+
+For testing purpose you should start VDR in foreground always. The plugin gives
+a lot of additional information to the console. This may be helpful in case it
+doesn't work at once.
+
+
+
+How to setup for VDR 1.4.x ?
+----------------------------
+
+Additional to the points mentioned above, you have to patch the VDR core with
+the supplied patch (vdr-1.4.x-sc7.diff). Recompile VDR and use the new binary.
+Patches from older SC releases are not going to work.
+
+Even with VDR 1.4.x you don't have to use a softcsa/ffdecsa patch.
+
+Activating/deactivating DVB cards in the plugin setup menu needs a VDR restart
+to take effect.
+
+
+
+Pre-compiled libraries
+----------------------
+
+
+There is the possibility that encryption systems are provided in binary, pre-
+compiled only form. During make process, all pre-compiled libraries are copied
+to your VDR plugin lib directory.
+
+Please be aware, that pre-compiled libraries are more or less bound to the hard-
+& software configuration they have been build on. Currently the build system is
+Intel 32bit, gcc 3.2.2, glibc 2.2.5. If your system differs too much, it may be
+impossible to use the pre-compiled libraries.
+
+Obviously, pre-compiled libraries cannot be exchanged between different SC
+and/or VDR API versions. Be aware that if you patch your VDR core and this patch
+involves changes to header files (*.h) this might change the VDR API even if the
+API version number hasn't changed. This may lead to silent malfunction/failure
+of pre-compiled libraries. In particular you should stay away from thread.h and
+tools.h as classes from there are used at many, many places.
+
+The naming scheme for the libraries is libsc-<MODULE>-<SCAPI>.so.<APIVERSION>,
+e.g. libsc-cardclient-2.so.1.3.47
+
+
+
+CAID and VDR CICAM setup
+------------------------
+
+The activation of the SC is controlled by your CICAM setup. As general setup
+(which is not SC specific) you should leave the CA values (in channels.conf)
+set to zero and let VDR's channel scanner (autopid) fill in the correct values.
+Don't touch the CA values afterwards.
+In the plugin setup menu, you now have to specify for which DVB cards the SC
+should be activated. The first two cards can be setup from the menu. If you
+need more, you can edit the setup.conf file manualy and add up to 10 cards.
+
+A real hardware CAM normaly knows which CAIDs it supports. With SC the situation
+is a bit different. There is support for a wide range of encryption system and
+cardclients. This results in a huge number of supported CAIDs, but for most of
+them it's uncertain if SC will actualy be able to decrypt a channel for them.
+On the other hand VDR limits the number of supported CAIDs to 16 for a CAM slot,
+so SC is able to announce a small number of CAIDs only. This is not as bad as it
+sounds, as VDR will try a CAM if ANY of the channel CAIDs matches the CAIDs
+announced by the CAM.
+On startup and at regular intervals the plugin scans the channels list and
+builds a chain of CAIDs. The CAIDs are assigned to the simulated CAM.
+
+To reduce the number of CAIDs SC has to deal with, you should obey some rules:
+-Remove all libsc-* files for encryption system which you don't intend to use
+ (e.g. SHL seems pretty useless nowadays).
+-When using a cardclient, be as precise as possible with the CAID/MASK values in
+ the cardclient.conf file. Using wide open 0000/0000 is deprecated.
+-Add CAIDs which you cannot use due to lack of keys to the SC.CaIgnore setting.
+
+
+
+
+Concurrent Recordings
+---------------------
+
+There is an entries in the plugin setup menu to control concurrent usage of a
+full-featured DVB card. You should enable concurrent usage only if you are using
+the special patched firmware AND a patched DVB driver. Note that toggling the
+flag will take effect the next time the plugin is idle on that specific DVB card
+only (i.e. no channel is being decrypted).
+
+There is no possibility to limit the number of concurrent streams. VDR itself
+has no limit in concurrent streams (neither FTA nor encrypted) and as the VDR
+core control all aspects of operation, there is no way to enforce a limit
+(beside disabling concurrent encrypted streams at all).
+
+
+
+Additional files
+----------------
+
+All config files must be located in a subdirectory (of your VDR config
+directory) called "plugins". The private plugin cache file is saved to this
+directory too. The keyfile must be named "SoftCam.Key".
+
+For Seca2 support you need binary files which contain the hash & mask tables.
+The file format is the same as for Yankse. The files must be located in the
+"plugins/seca" subdirectory. The name sheme is s2_TTTT_XXXX.bin where TTTT is
+one of "hash","mt" and XXXX is the provider ID (e.g. s2_hash_0064.bin,
+s2_mt_0070.bin). The hash file must be 1536 bytes long. The mt file is normaly
+16384 bytes long, but this may differ for your provider. For advanced Seca2
+providers you may need additional table files. At the moment these are
+s2_sse.bin, s2_sse_XXXX.bin and s2_cw_XXXX.bin.
+
+Although there is support for Viaccess TPS AU, you still can provide a "tps.bin"
+file in the "plugins/viaccess" subdirectory in case AU doesn't work for your
+provider. Be aware that in most cases this file has to be updated on a daily
+basis.
+
+Note, that for this @SHL implementation the key must be in Z 00 00 <key> format
+(the V 000000 00 <key> format doesn't work).
+
+For Irdeto, Seca and Viaccess AU you need valid subscription card data, which
+have to be located in the files "Ird-Beta.KID", "Seca.KID" or "Viaccess.KID".
+See the files in the "examples" subdirectory for file formats.
+
+For Nagra1 AU you need appropriate binary Rom and Eeprom files. The files have
+to be named "ROMX.bin", "ROMXext.bin" or "eepX_Z.bin", where X is the ROM number
+(decimal) and Z is the upper part of the provider ID (hexadecimal). The Eeprom
+files may be updated from the EMM data, take care that the permissions are set
+right. The plugin searches for these files in the "plugins/nagra" subdirectory.
+
+For Nagra2 AU some providers need binary Rom and Eeprom files. The files have to
+be named "ROMxxx.bin" and "EEPyy_xxx.bin", where xxx is the ROM version (e.g.
+102) and yy is the upper part of the provider ID (e.g. 08 for BEV). The files
+must contain the joined contents of all Rom/Eeprom pages. The plugin searches
+for these files in the "plugins/nagra" subdirectory.
+
+
+
+External key updates
+--------------------
+
+If key updates are available from external sources (e.g. website) only, they may
+be feed from a shell script. To enable this, you have to specify the script name
+with commandline option "-E". The script will be called at a regular interval
+(currently 15 minutes) or whenever a needed key is not available (but not more
+often than every 2 minutes). The script has to output the keys to it's stdout in
+the same format as for the key file. The script may output several keys in one
+call (each key on a seperate line). You can find an example script in the
+"examples" subdirectory.
+
+
+
+Smartcard support
+-----------------
+
+For most encrpytion systems this plugin supports original subscription
+smartcards on a Phoenix/Smartmouse ISO interface connected to a serial port.
+
+To enable smartcard support you have to copy one or more of the smartcard
+systems to the VDR plugin lib directory. To actually activate the smartcard
+interface, you should use the commandline option "-s" to specify one or more
+serial devices to which the Phoenix interface are connected e.g. use "-s
+/dev/ttyS0 -s /dev/ttyS1" to use two intefaces at COM1/COM2. If you want to add
+a default smartcard interface at compile time use the make option DEFAULT_PORT,
+e.g. DEFAULT_PORT='"/dev/ttyS0",0,0,0'. Note the quotes and double quotes. The
+three numeric values are identical to the -I and -R options (set to 1 to enable)
+and -C option (set to 0 for default clock) below.
+
+Appearently there are "broken" card readers which swap the meaning of the CD
+line (used for card detection). For these readers use the option "-I". This
+enables inverse CD detection for the next interface e.g. "-I -s /dev/ttyS0 -s
+/dev/ttyS1" will use inverse CD on COM1 and normal CD on COM2 while "-I -s
+/dev/ttyS0 -I -s /dev/ttyS1" will use inverse CD on both.
+Some other card readers have a reversed logic with the reset line (card won't
+reset with default settings). You can use the option "-R" for these readers.
+In some cases it's mandatory to know the exact clock frequency at which your
+cardreader runs (e.g. for baudrate calculations). With the option "-C" you can
+give a clock frequency (in Hz) which will be used instead of the default
+(3571200 Hz) for the next interface e.g. "-C 3579545 -s /dev/ttyS1".
+
+Some smartcards need additional information to establish communication with the
+card (e.g. certificate or box key for camcrypt). These information must be
+available in the "smartcard.conf" file (see example file for format) or you card
+won't work correctly.
+
+If you insert a card into a interface the card is autodetected (your interface
+should use the CD line to signal card presence or it won't work) and
+initialised (this may take some seconds to complete). You can use the setup
+menu to see which cards are currently inserted and detected. You can remove a
+smartcard at any time without prior action, but of course this will disrupt
+decryption if you are tuned to a channel which requires the card.
+
+
+
+Cardserver client
+-----------------
+
+The cardclient is a client for several cardservers. Supported cardservers are :
+radegast, newcamd, camd33 (tcp), camd35 (udp), cardd, buffy and aroureos.
+
+You can configure as many clients for different servers as you want. The client
+configuration is read from the file "cardclient.conf". Every line in the file
+defines a client-server connection. The line starts with the client name and is
+followed by additional arguments which depend on the client type. See the file
+"examples/cardclient.conf.example" for format and arguments.
+
+The connections are tried in the order they are defined in the conf file until
+a valid decryption is obtained. After that, the decryption sticks to that
+particular connection until next channel switch.
+
+The network code supports dialup on demand. To use this you have to provide an
+external script to connect/disconnect your dialup link. Use commandline option
+-d to set the script name and enable the feature, e.g. "-d dialup.sh". See the
+example script "examples/dialup.sh.example". The network is brought up as soon
+as an server connection is requested. All server connections are disconnected if
+they are idle too long (normaly after 120 seconds). The network is brought down
+after the last connection has terminated and an additional network timeout has
+expired. The network timeout is configurable with the commandline option -t and
+defaults to 60 seconds, e.g. "-t 120".
+
+The current cardclient implementation is loosely based on the Streamboard
+client (contributed by S.Laurel from the Streamboard), which was included in
+earlier releases.
+
+
+
+SVDR interface
+--------------
+
+The plugin implements a SVDR interface. Supported commands are:
+ RELOAD
+ reload all configuration files (only if the softcam isn't active at the
+ moment).
+ Return codes: 550 - Softcam active, can't reload files.
+ 901 - Reloading files not entirely successfull. Most of the
+ time this will leave you with an unusable softcam.
+ 900 - Reload successfull.
+
+ LOG [on|off] module.option[,module.option][,...]
+ Enables or disables all given message classes.
+ Return codes: 501 - Syntax error or unknown message class.
+ 900 - Options set and saved.
+
+ LOGCFG
+ Display all available message classes and report their status. This can be
+ usefull if you want to provide an external GUI or whatever to handle the
+ message classes.
+ Return codes: 901 - No message classes available.
+ 900 - Message class status (multi line reply).
--- /dev/null
+
+FFdecsa implementation
+======================
+
+This plugin package contains a FFdecsa implementation for software descrambling
+on budget cards. On full featured cards this is done by the hardware ECD chip.
+
+FFdecsa only works with budget cards (a full featured card isn't able to deliver
+an encrypted stream due to hardware design).
+
+This package uses code from the great FFdecsa-1.0.0 package. FFdecsa-1.0.0 is
+copyright by fatih89r and released under the GPL. Many thanks for this
+outstanding work! You'll find the slightly modified FFdecsa code in the FFdecsa
+directory.
+
+There are several options to configure optimization at compile time. Use:
+
+CPUOPT=type to determine your CPU type. With gcc 3.2 valid options are:
+ pentium, pentium-mmx, pentiumpro, pentium2, pentium3, pentium4,
+ k6, k6-2, k6-3, athlon, athlon-tbird, athlon4, athlon-xp and
+ athlon-mp. You should set your CPU type as close as possible for
+ best performance. Defaults to pentium.
+
+PARALLEL=mode to determine which datatype should be used for parallel
+ processing. Valid modes are:
+ PARALLEL_32_4CHAR
+ PARALLEL_32_4CHARA
+ PARALLEL_32_INT
+ PARALLEL_64_8CHAR
+ PARALLEL_64_8CHARA
+ PARALLEL_64_2INT
+ PARALLEL_64_LONG
+ PARALLEL_64_MMX
+ PARALLEL_128_16CHAR
+ PARALLEL_128_16CHARA
+ PARALLEL_128_4INT
+ PARALLEL_128_2LONG
+ PARALLEL_128_2MMX
+ PARALLEL_128_SSE
+ Hints: if you have a Pentium4 or AthlonXP and a recent compiler
+ try PARALLEL_64_MMX. If you have a 64-bit CPU, try
+ PARALLEL_128_SSE. If you're unsure take PARALLEL_32_INT (which
+ isn't the fastest, but quite good and very portable).
+ For additional information read the FFdecsa documentation.
+ Defaults to PARALLEL_32_INT.
+
+CSAFLAGS=opts to set compiler options.
+ Defaults to -Wall -fPIC -g -O3 -mmmx -fomit-frame-pointer
+ -fexpensive-optimizations -funroll-loops
+
+All of them can be added to VDR's make.config file.
+
+
+
+History:
+--------
+
+30.01.2007:
+- Integrated into SC plugin as of version 0.7.0. Future changes will be reported
+ in HISTORY file.
+
+02.01.2006: Version 0.1.3
+- Now locking the CSA engine properly to prevent potentional problems when
+ updating CWs during decryption. Thanks to NooneImportant for pointing this
+ out.
+- Added -fPIC to default compiler flags.
+
+09.11.2005: Version 0.1.2
+- Fixed segfault when starting VDR with devices in non-native sequence i.e. by
+ using -D switches. Thanks to noneed for reporting.
+
+03.10.2005: Version 0.1.1
+- Removed static vars from stream code. Caused glitches when using two budgets
+ cards concurrently. Thanks to dingo35 for reporting.
+- Some small performace improvements. Thanks to dingo35.
+- Removed ugly tmp_autogenerated stuff.
+
+18.09.2005: Version 0.1.0
+- Initial version.
+- Supporting concurrent decryption.
+- Requires sc plugin 0.5.0 or newer.
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <dlfcn.h>
+
+#include <linux/dvb/ca.h>
+#include <vdr/ci.h>
+#include <vdr/dvbdevice.h>
+#if APIVERSNUM >= 10500
+#include <vdr/dvbci.h>
+#endif
+#include <vdr/channels.h>
+#include <vdr/thread.h>
+
+#include "FFdecsa/FFdecsa.h"
+
+#include "cam.h"
+#include "scsetup.h"
+#include "filter.h"
+#include "system.h"
+#include "data.h"
+#include "misc.h"
+#include "log-core.h"
+
+#define IDLE_SLEEP 0 // idleTime when sleeping
+#define IDLE_GETCA 200 // idleTime when waiting for ca descriptors
+#define IDLE_GETCA_SLOW 20000 // idleTime if no enc. system
+#define IDLE_NO_SYNC 800 // idleTime when not in sync
+#define IDLE_SYNC 2000 // idleTime when in sync
+
+#define CW_REPEAT_TIME 2000 // rewrite CW after X ms
+#define LOG_COUNT 3 // stop logging after X complete ECM cycles
+#define CHAIN_HOLD 120000 // min. time to hold a logger chain
+#define ECM_DATA_TIME 6000 // time to wait for ECM data updates
+#define ECM_UPD_TIME 120000 // delay between ECM data updates
+#define MAX_ECM_IDLE 300000 // delay before an idle handler can be removed
+#define MAX_ECM_HOLD 15000 // delay before an idle handler stops processing
+#define CAID_TIME 300000 // time between caid scans
+
+#define L_HEX 2
+#define L_HEX_ECM LCLASS(L_HEX,2)
+#define L_HEX_EMM LCLASS(L_HEX,4)
+#define L_HEX_CAT LCLASS(L_HEX,8)
+#define L_HEX_PMT LCLASS(L_HEX,16)
+#define L_HEX_HOOK LCLASS(L_HEX,32)
+#define L_HEX_ALL LALL(L_HEX_HOOK)
+
+static const struct LogModule lm_hex = {
+ (LMOD_ENABLE|L_HEX_ALL)&LOPT_MASK,
+ (LMOD_ENABLE)&LOPT_MASK,
+ "hexdata",
+ { "ecm","emm","cat","pmt","hook" }
+ };
+ADD_MODULE(L_HEX,lm_hex)
+
+static const char *typeNames[] = { "typ0","typ1","VIDEO","typ3","AUDIO","typ5","DOLBY","typ6+" };
+#define TYPENAME(type) (typeNames[(type)<=7?(type):7])
+
+// -- cLogStats ---------------------------------------------------------------
+
+#define COUNTS 20
+#define SAMPLE (30*1000)
+#define AVR1 (60*1000)
+#define AVR2 (4*60*1000)
+#define AVR3 (10*60*1000)
+#define REPORT (60*1000)
+
+class cLogStats : public cThread {
+private:
+ cTimeMs sTime, repTime;
+ int sCount, sIdx, sCounts[COUNTS][2];
+protected:
+ virtual void Action(void);
+public:
+ cLogStats(void);
+ ~cLogStats();
+ void Count(void);
+ };
+
+static cMutex logstatsMutex;
+static cLogStats *logstats=0;
+
+void LogStatsUp(void)
+{
+ logstatsMutex.Lock();
+ if(LOG(L_CORE_AUSTATS) && !logstats) logstats=new cLogStats;
+ logstatsMutex.Unlock();
+}
+
+void LogStatsDown(void)
+{
+ logstatsMutex.Lock();
+ if(logstats) { delete logstats; logstats=0; }
+ logstatsMutex.Unlock();
+}
+
+cLogStats::cLogStats(void)
+{
+ sCount=sIdx=0;
+ for(int i=0; i<COUNTS; i++) { sCounts[i][0]=0; sCounts[i][1]=SAMPLE; }
+ SetDescription("logger stats");
+ Start();
+}
+
+cLogStats::~cLogStats()
+{
+ Cancel(2);
+}
+
+void cLogStats::Count(void)
+{
+ sCount++;
+}
+
+void cLogStats::Action(void)
+{
+ while(Running()) {
+ cCondWait::SleepMs(50);
+ if(sTime.Elapsed()>SAMPLE) {
+ sCounts[sIdx][0]=sCount; sCount=0;
+ sCounts[sIdx][1]=sTime.Elapsed(); sTime.Set();
+ if(++sIdx >= COUNTS) sIdx=0;
+ }
+ if(repTime.Elapsed()>REPORT) {
+ repTime.Set();
+ if(sCounts[(sIdx+COUNTS-1)%COUNTS][0]>0) {
+ LBSTART(L_CORE_AUSTATS);
+ LBPUT("EMM packet load average (%d/%d/%dmin)",AVR1/60000,AVR2/60000,AVR3/60000);
+ int s=0, t=0;
+ for(int i=1; i<=COUNTS; i++) {
+ s+=sCounts[(sIdx+COUNTS-i)%COUNTS][0];
+ t+=sCounts[(sIdx+COUNTS-i)%COUNTS][1];
+ if(i==(AVR1/SAMPLE) || i==(AVR2/SAMPLE) || i==(AVR3/SAMPLE))
+ LBPUT(" %4d",(int)((float)s/(float)t*1000.0));
+ }
+ LBPUT(" pks/s");
+ LBEND();
+ }
+ }
+ }
+}
+
+// -- cHookManager -------------------------------------------------------------
+
+class cHookManager : public cAction {
+ int cardNum;
+ cSimpleList<cLogHook> hooks;
+ //
+ cPidFilter *AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc);
+ void ClearHooks(void);
+ void DelHook(cLogHook *hook);
+protected:
+ virtual void Process(cPidFilter *filter, unsigned char *data, int len);
+public:
+ cHookManager(int CardNum);
+ virtual ~cHookManager();
+ void AddHook(cLogHook *hook);
+ bool TriggerHook(int id);
+ void Down(void);
+ };
+
+cHookManager::cHookManager(int CardNum)
+:cAction("hookmanager",CardNum)
+{
+ cardNum=CardNum;
+ Priority(10);
+}
+
+cHookManager::~cHookManager()
+{
+ Down();
+}
+
+void cHookManager::Down(void)
+{
+ Lock();
+ while(cLogHook *hook=hooks.First()) DelHook(hook);
+ DelAllFilter();
+ Unlock();
+}
+
+bool cHookManager::TriggerHook(int id)
+{
+ Lock();
+ for(cLogHook *hook=hooks.First(); hook; hook=hooks.Next(hook))
+ if(hook->id==id) {
+ hook->delay.Set(CHAIN_HOLD);
+ Unlock();
+ return true;
+ }
+ Unlock();
+ return false;
+}
+
+void cHookManager::AddHook(cLogHook *hook)
+{
+ Lock();
+ PRINTF(L_CORE_HOOK,"%d: starting hook '%s' (%04x)",cardNum,hook->name,hook->id);
+ hook->delay.Set(CHAIN_HOLD);
+ hook->cardNum=cardNum;
+ hooks.Add(hook);
+ for(cPid *pid=hook->pids.First(); pid; pid=hook->pids.Next(pid)) {
+ cPidFilter *filter=AddFilter(pid->pid,pid->sct,pid->mask,pid->mode,CHAIN_HOLD/8,false);
+ if(filter) {
+ filter->userData=(void *)hook;
+ pid->filter=filter;
+ }
+ }
+ Unlock();
+}
+
+void cHookManager::DelHook(cLogHook *hook)
+{
+ PRINTF(L_CORE_HOOK,"%d: stopping hook '%s' (%04x)",cardNum,hook->name,hook->id);
+ for(cPid *pid=hook->pids.First(); pid; pid=hook->pids.Next(pid)) {
+ cPidFilter *filter=pid->filter;
+ if(filter) {
+ DelFilter(filter);
+ pid->filter=0;
+ }
+ }
+ hooks.Del(hook);
+}
+
+cPidFilter *cHookManager::AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc)
+{
+ cPidFilter *filter=NewFilter(IdleTime);
+ if(filter) {
+ filter->SetBuffSize(32768);
+ filter->Start(Pid,Section,Mask,Mode,Crc);
+ PRINTF(L_CORE_HOOK,"%d: added filter pid=0x%.4x sct=0x%.2x/0x%.2x/0x%.2x idle=%d crc=%d",cardNum,Pid,Section,Mask,Mode,IdleTime,Crc);
+ }
+ else PRINTF(L_GEN_ERROR,"no free slot or filter failed to open for hookmanager %d",cardNum);
+ return filter;
+}
+
+void cHookManager::Process(cPidFilter *filter, unsigned char *data, int len)
+{
+ if(data && len>0) {
+ HEXDUMP(L_HEX_HOOK,data,len,"HOOK pid 0x%04x",filter->Pid());
+ if(SCT_LEN(data)==len) {
+ cLogHook *hook=(cLogHook *)(filter->userData);
+ if(hook) {
+ hook->Process(filter->Pid(),data);
+ if(hook->bailOut || hook->delay.TimedOut()) DelHook(hook);
+ }
+ }
+ else PRINTF(L_CORE_HOOK,"%d: incomplete section %d != %d",cardNum,len,SCT_LEN(data));
+ }
+ else {
+ cLogHook *hook=(cLogHook *)(filter->userData);
+ if(hook && (hook->bailOut || hook->delay.TimedOut())) DelHook(hook);
+ }
+}
+
+// -- cLogChain ----------------------------------------------------------------
+
+class cLogChain : public cSimpleItem {
+public:
+ int cardNum, caid;
+ bool softCSA, active, delayed;
+ cTimeMs delay;
+ cPids pids;
+ cSimpleList<cSystem> systems;
+ //
+ cLogChain(int CardNum, bool soft);
+ void Process(int pid, unsigned char *data);
+ bool Parse(const unsigned char *cat);
+ };
+
+cLogChain::cLogChain(int CardNum, bool soft)
+{
+ cardNum=CardNum; softCSA=soft; active=delayed=false;
+}
+
+void cLogChain::Process(int pid, unsigned char *data)
+{
+ if(active) {
+ for(cSystem *sys=systems.First(); sys; sys=systems.Next(sys))
+ sys->ProcessEMM(pid,caid,data);
+ }
+}
+
+bool cLogChain::Parse(const unsigned char *cat)
+{
+ if(cat[0]==0x09) {
+ caid=WORD(cat,2,0xFFFF);
+ LBSTARTF(L_CORE_AU);
+ LBPUT("%d: chain caid %04x",cardNum,caid);
+ cSystem *sys;
+ if(systems.Count()>0) {
+ LBPUT(" ++");
+ for(sys=systems.First(); sys; sys=systems.Next(sys))
+ sys->ParseCAT(&pids,cat);
+ }
+ else {
+ LBPUT(" ->");
+ int Pri=0;
+ while((sys=cSystems::FindBySysId(caid,!softCSA,Pri))) {
+ Pri=sys->Pri();
+ if(sys->HasLogger()) {
+ sys->CardNum(cardNum);
+ sys->ParseCAT(&pids,cat);
+ systems.Add(sys);
+ LBPUT(" %s(%d)",sys->Name(),sys->Pri());
+ }
+ else
+ delete sys;
+ }
+ }
+ if(systems.Count()==0) LBPUT(" none available");
+ for(cPid *pid=pids.First(); pid; pid=pids.Next(pid))
+ LBPUT(" [%04x-%02x/%02x/%02x]",pid->pid,pid->sct,pid->mask,pid->mode);
+ LBEND();
+ if(systems.Count()>0 && pids.Count()>0)
+ return true;
+ }
+ return false;
+}
+
+// -- cLogger ------------------------------------------------------------------
+
+class cLogger : public cAction {
+private:
+ int cardNum;
+ bool softCSA, up;
+ cSimpleList<cLogChain> chains;
+ cSimpleList<cEcmInfo> active;
+ //
+ cPidFilter *catfilt;
+ int catVers;
+ //
+ cPidFilter *AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc);
+ void SetChains(void);
+ void ClearChains(void);
+ void StartChain(cLogChain *chain);
+ void StopChain(cLogChain *chain, bool force);
+protected:
+ virtual void Process(cPidFilter *filter, unsigned char *data, int len);
+public:
+ cLogger(int CardNum, bool soft);
+ virtual ~cLogger();
+ void EcmStatus(const cEcmInfo *ecm, bool on);
+ void Up(void);
+ void Down(void);
+ };
+
+cLogger::cLogger(int CardNum, bool soft)
+:cAction("logger",CardNum)
+{
+ cardNum=CardNum; softCSA=soft;
+ catfilt=0; up=false;
+ Priority(10);
+}
+
+cLogger::~cLogger()
+{
+ Down();
+}
+
+void cLogger::Up(void)
+{
+ Lock();
+ if(!up) {
+ PRINTF(L_CORE_AUEXTRA,"%d: UP",cardNum);
+ catVers=-1;
+ catfilt=AddFilter(1,0x01,0xFF,0,0,true);
+ up=true;
+ }
+ Unlock();
+}
+
+void cLogger::Down(void)
+{
+ Lock();
+ if(up) {
+ PRINTF(L_CORE_AUEXTRA,"%d: DOWN",cardNum);
+ ClearChains();
+ DelAllFilter();
+ catfilt=0; up=false;
+ }
+ Unlock();
+}
+
+void cLogger::EcmStatus(const cEcmInfo *ecm, bool on)
+{
+ Lock();
+ PRINTF(L_CORE_AUEXTRA,"%d: ecm prgid=%d caid=%04x prov=%.4x %s",cardNum,ecm->prgId,ecm->caId,ecm->provId,on ? "active":"inactive");
+ cEcmInfo *e;
+ if(on) {
+ e=new cEcmInfo(ecm);
+ active.Add(e);
+ if(!up) Up();
+ }
+ else {
+ for(e=active.First(); e; e=active.Next(e))
+ if(e->Compare(ecm)) {
+ active.Del(e);
+ break;
+ }
+ }
+ SetChains();
+ Unlock();
+}
+
+void cLogger::SetChains(void)
+{
+ for(cLogChain *chain=chains.First(); chain; chain=chains.Next(chain)) {
+ bool act=false;
+ if(ScSetup.AutoUpdate>1) act=true;
+ else if(ScSetup.AutoUpdate==1) {
+ for(cEcmInfo *e=active.First(); e; e=active.Next(e))
+ if((e->emmCaId && chain->caid==e->emmCaId) || chain->caid==e->caId) {
+ act=true; break;
+ }
+ }
+ if(act) StartChain(chain);
+ else StopChain(chain,false);
+ }
+}
+
+void cLogger::ClearChains(void)
+{
+ for(cLogChain *chain=chains.First(); chain; chain=chains.Next(chain))
+ StopChain(chain,true);
+ chains.Clear();
+}
+
+void cLogger::StartChain(cLogChain *chain)
+{
+ if(chain->delayed)
+ PRINTF(L_CORE_AUEXTRA,"%d: restarting delayed chain %04x",cardNum,chain->caid);
+ chain->delayed=false;
+ if(!chain->active) {
+ PRINTF(L_CORE_AU,"%d: starting chain %04x",cardNum,chain->caid);
+ chain->active=true;
+ for(cPid *pid=chain->pids.First(); pid; pid=chain->pids.Next(pid)) {
+ cPidFilter *filter=AddFilter(pid->pid,pid->sct,pid->mask,pid->mode,CHAIN_HOLD/8,false);
+ if(filter) {
+ filter->userData=(void *)chain;
+ pid->filter=filter;
+ }
+ }
+ }
+}
+
+void cLogger::StopChain(cLogChain *chain, bool force)
+{
+ if(chain->active) {
+ if(force || (chain->delayed && chain->delay.TimedOut())) {
+ PRINTF(L_CORE_AU,"%d: stopping chain %04x",cardNum,chain->caid);
+ chain->active=false;
+ for(cPid *pid=chain->pids.First(); pid; pid=chain->pids.Next(pid)) {
+ cPidFilter *filter=pid->filter;
+ if(filter) {
+ DelFilter(filter);
+ pid->filter=0;
+ }
+ }
+ }
+ else if(!chain->delayed) {
+ PRINTF(L_CORE_AUEXTRA,"%d: delaying chain %04x",cardNum,chain->caid);
+ chain->delayed=true;
+ chain->delay.Set(CHAIN_HOLD);
+ }
+ }
+}
+
+cPidFilter *cLogger::AddFilter(int Pid, int Section, int Mask, int Mode, int IdleTime, bool Crc)
+{
+ cPidFilter *filter=NewFilter(IdleTime);
+ if(filter) {
+ if(Pid>1) filter->SetBuffSize(32768);
+ filter->Start(Pid,Section,Mask,Mode,Crc);
+ PRINTF(L_CORE_AUEXTRA,"%d: added filter pid=0x%.4x sct=0x%.2x/0x%.2x/0x%.2x idle=%d crc=%d",cardNum,Pid,Section,Mask,Mode,IdleTime,Crc);
+ }
+ else PRINTF(L_GEN_ERROR,"no free slot or filter failed to open for logger %d",cardNum);
+ return filter;
+}
+
+void cLogger::Process(cPidFilter *filter, unsigned char *data, int len)
+{
+ if(data && len>0) {
+ if(filter==catfilt) {
+ int vers=(data[5]&0x3E)>>1;
+ if(data[0]==0x01 && vers!=catVers) {
+ PRINTF(L_CORE_AUEXTRA,"%d: got CAT version %02x",cardNum,vers);
+ catVers=vers;
+ HEXDUMP(L_HEX_CAT,data,len,"CAT vers %02x",catVers);
+ ClearChains();
+ for(int i=8; i<len-4; i+=data[i+1]+2) {
+ if(data[i]==0x09) {
+ int caid=WORD(data,i+2,0xFFFF);
+ cLogChain *chain;
+ for(chain=chains.First(); chain; chain=chains.Next(chain))
+ if(chain->caid==caid) break;
+ if(chain)
+ chain->Parse(&data[i]);
+ else {
+ chain=new cLogChain(cardNum,softCSA);
+ if(chain->Parse(&data[i]))
+ chains.Add(chain);
+ else
+ delete chain;
+ }
+ }
+ }
+ SetChains();
+ }
+ }
+ else {
+ HEXDUMP(L_HEX_EMM,data,len,"EMM pid 0x%04x",filter->Pid());
+ if(logstats) logstats->Count();
+ if(SCT_LEN(data)==len) {
+ cLogChain *chain=(cLogChain *)(filter->userData);
+ if(chain) {
+ chain->Process(filter->Pid(),data);
+ if(chain->delayed) StopChain(chain,false);
+ }
+ }
+ else PRINTF(L_CORE_AU,"%d: incomplete section %d != %d",cardNum,len,SCT_LEN(data));
+ }
+ }
+ else {
+ cLogChain *chain=(cLogChain *)(filter->userData);
+ if(chain && chain->delayed) StopChain(chain,false);
+ }
+}
+
+// -- cEcmData -----------------------------------------------------------------
+
+class cEcmData : public cEcmInfo {
+private:
+ bool del;
+public:
+ cEcmData(void);
+ cEcmData(cEcmInfo *e);
+ bool Save(FILE *f);
+ bool Parse(const char *buf);
+ void Delete(void) { del=true; }
+ bool IsDeleted(void) const { return del; }
+ };
+
+cEcmData::cEcmData(void)
+:cEcmInfo()
+{
+ del=false;
+}
+
+cEcmData::cEcmData(cEcmInfo *e)
+:cEcmInfo(e)
+{
+ del=false;
+}
+
+bool cEcmData::Parse(const char *buf)
+{
+ char Name[64];
+ int nu=0, num;
+ Name[0]=0;
+ if(sscanf(buf,"%d:%x:%x:%63[^:]:%x/%x:%x:%x/%x:%d%n",&prgId,&source,&transponder,Name,&caId,&emmCaId,&provId,&ecm_pid,&ecm_table,&nu,&num)>=9) {
+ SetName(Name);
+ const char *line=buf+num;
+ if(nu>0 && *line++==':') {
+ unsigned char dat[nu];
+ if(GetHex(line,dat,nu,true)==nu) AddData(dat,nu);
+ }
+ return true;
+ }
+ return false;
+}
+
+bool cEcmData::Save(FILE *f)
+{
+ fprintf(f,"%d:%x:%x:%s:%x/%x:%x:%x/%x",prgId,source,transponder,name,caId,emmCaId,provId,ecm_pid,ecm_table);
+ if(data) {
+ char str[dataLen*2+2];
+ fprintf(f,":%d:%s\n",dataLen,HexStr(str,data,dataLen));
+ }
+ else
+ fprintf(f,":0\n");
+ return ferror(f)==0;
+}
+
+// -- cEcmCache ----------------------------------------------------------------
+
+cEcmCache ecmcache;
+
+cEcmCache::cEcmCache(void)
+:cLoader("ECM")
+{}
+
+void cEcmCache::New(cEcmInfo *e)
+{
+ Lock();
+ cEcmData *dat;
+ if(!(dat=Exists(e))) {
+ dat=new cEcmData(e);
+ Add(dat);
+ Modified();
+ PRINTF(L_CORE_ECM,"cache add prgId=%d source=%x transponder=%x ecm=%x/%x",e->prgId,e->source,e->transponder,e->ecm_pid,e->ecm_table);
+ }
+ else {
+ if(strcasecmp(e->name,dat->name)) {
+ dat->SetName(e->name);
+ Modified();
+ }
+ if(dat->Update(e))
+ Modified();
+ }
+ e->SetCached();
+ Unlock();
+}
+
+cEcmData *cEcmCache::Exists(cEcmInfo *e)
+{
+ for(cEcmData *dat=First(); dat; dat=Next(dat))
+ if(!dat->IsDeleted() && dat->Compare(e)) return dat;
+ return 0;
+}
+
+int cEcmCache::GetCached(cSimpleList<cEcmInfo> *list, int sid, int Source, int Transponder)
+{
+ int n=0;
+ list->Clear();
+ Lock();
+ for(cEcmData *dat=First(); dat; dat=Next(dat)) {
+ if(!dat->IsDeleted() && dat->prgId==sid && dat->source==Source && dat->transponder==Transponder) {
+ cEcmInfo *e=new cEcmInfo(dat);
+ if(e) {
+ PRINTF(L_CORE_ECM,"from cache: system %s (%04x) id %04x with ecm %x/%x",e->name,e->caId,e->provId,e->ecm_pid,e->ecm_table);
+ e->SetCached();
+ list->Add(e);
+ n++;
+ }
+ }
+ }
+ Unlock();
+ return n;
+}
+
+void cEcmCache::Delete(cEcmInfo *e)
+{
+ Lock();
+ cEcmData *dat=Exists(e);
+ if(dat) {
+ dat->Delete();
+ Modified();
+ PRINTF(L_CORE_ECM,"invalidated cached prgId=%d source=%x transponder=%x ecm=%x/%x",dat->prgId,dat->source,dat->transponder,dat->ecm_pid,dat->ecm_table);
+ }
+ Unlock();
+}
+
+void cEcmCache::Flush(void)
+{
+ Lock();
+ Clear();
+ Modified();
+ PRINTF(L_CORE_ECM,"cache flushed");
+ Unlock();
+}
+
+void cEcmCache::Load(void)
+{
+ Lock();
+ Clear();
+ Unlock();
+}
+
+bool cEcmCache::Save(FILE *f)
+{
+ bool res=true;
+ Lock();
+ for(cEcmData *dat=First(); dat;) {
+ if(!dat->IsDeleted()) {
+ if(!dat->Save(f)) { res=false; break; }
+ dat=Next(dat);
+ }
+ else {
+ cEcmData *n=Next(dat);
+ Del(dat);
+ dat=n;
+ }
+ }
+ Modified(!res);
+ Unlock();
+ return res;
+}
+
+bool cEcmCache::ParseLine(const char *line, bool fromCache)
+{
+ bool res=false;
+ cEcmData *dat=new cEcmData;
+ if(dat && dat->Parse(line)) {
+ if(!Exists(dat)) { Add(dat); dat=0; }
+ res=true;
+ }
+ delete dat;
+ return res;
+}
+
+// -- cPrgPid ---------------------------------------------------------------------
+
+class cPrgPid : public cSimpleItem {
+private:
+ int type;
+ int pid;
+ bool proc;
+public:
+ cPrgPid(int Type, int Pid);
+ int Pid(void) { return pid; }
+ int Type(void) { return type; }
+ bool Proc(void) { return proc; }
+ void Proc(bool is) { proc=is; };
+ };
+
+cPrgPid::cPrgPid(int Type, int Pid)
+{
+ type=Type; pid=Pid;
+ proc=false;
+}
+
+// -- cPrg ---------------------------------------------------------------------
+
+class cPrg : public cSimpleItem {
+private:
+ int prg;
+ bool isUpdate;
+public:
+ cSimpleList<cPrgPid> pids;
+ //
+ cPrg(int Prg, bool IsUpdate);
+ int Prg(void) { return prg; }
+ bool IsUpdate(void) { return isUpdate; }
+ };
+
+cPrg::cPrg(int Prg, bool IsUpdate)
+{
+ prg=Prg; isUpdate=IsUpdate;
+}
+
+// -- cEcmSys ------------------------------------------------------------------
+
+class cEcmPri : public cSimpleItem {
+public:
+ cEcmInfo *ecm;
+ int pri, sysIdent;
+ };
+
+// -- cEcmHandler --------------------------------------------------------------
+
+class cEcmHandler : public cSimpleItem, public cAction {
+private:
+ int cardNum, cwIndex;
+ cCam *cam;
+ char *id;
+ cTimeMs idleTime;
+ //
+ cMutex dataMutex;
+ int sid;
+ cSimpleList<cPrgPid> pids;
+ //
+ cSystem *sys;
+ cPidFilter *filter;
+ int filterCwIndex, filterSource, filterTransponder, filterSid;
+ unsigned char lastCw[16];
+ bool sync, noKey, trigger;
+ int triggerMode;
+ int mode, count;
+ cTimeMs lastsync, startecm, resendTime;
+ unsigned int cryptPeriod;
+ unsigned char parity;
+ cMsgCache failed;
+ //
+ cSimpleList<cEcmInfo> ecmList;
+ cSimpleList<cEcmPri> ecmPriList;
+ cEcmInfo *ecm;
+ cEcmPri *ecmPri;
+ cTimeMs ecmUpdTime;
+ //
+ int dolog;
+ //
+ void DeleteSys(void);
+ void NoSync(bool clearParity);
+ cEcmInfo *NewEcm(void);
+ cEcmInfo *JumpEcm(void);
+ void StopEcm(void);
+ bool UpdateEcm(void);
+ void EcmOk(void);
+ void EcmFail(void);
+ void ParseCAInfo(int sys);
+ void AddEcmPri(cEcmInfo *n);
+protected:
+ virtual void Process(cPidFilter *filter, unsigned char *data, int len);
+public:
+ cEcmHandler(cCam *Cam, int CardNum, int cwindex);
+ virtual ~cEcmHandler();
+ void Stop(void);
+ void SetPrg(cPrg *prg);
+ void ShiftCwIndex(int cwindex);
+ char *CurrentKeyStr(void) const;
+ bool IsRemoveable(void);
+ bool IsIdle(void);
+ int Sid(void) const { return sid; }
+ int CwIndex(void) const { return cwIndex; }
+ const char *Id(void) const { return id; }
+ };
+
+cEcmHandler::cEcmHandler(cCam *Cam, int CardNum, int cwindex)
+:cAction("ecmhandler",CardNum)
+,failed(32,0)
+{
+ cam=Cam;
+ cardNum=CardNum;
+ cwIndex=cwindex;
+ sys=0; filter=0; ecm=0; ecmPri=0; mode=-1; sid=-1;
+ trigger=false; triggerMode=-1;
+ filterSource=filterTransponder=0; filterCwIndex=-1; filterSid=-1;
+ asprintf(&id,"%d.%d",cardNum,cwindex);
+}
+
+cEcmHandler::~cEcmHandler()
+{
+ Lock();
+ StopEcm();
+ DelAllFilter(); // delete filters before sys for multi-threading reasons
+ DeleteSys();
+ Unlock();
+ free(id);
+}
+
+bool cEcmHandler::IsIdle(void)
+{
+ dataMutex.Lock();
+ int n=pids.Count();
+ dataMutex.Unlock();
+ return n==0;
+}
+
+bool cEcmHandler::IsRemoveable(void)
+{
+ return IsIdle() && idleTime.Elapsed()>MAX_ECM_IDLE;
+}
+
+void cEcmHandler::Stop(void)
+{
+ dataMutex.Lock();
+ if(!IsIdle() || sid!=-1) {
+ PRINTF(L_CORE_ECM,"%s: stop",id);
+ sid=-1;
+ idleTime.Set();
+ pids.Clear();
+ trigger=true;
+ }
+ dataMutex.Unlock();
+ if(filter) filter->Wakeup();
+}
+
+void cEcmHandler::ShiftCwIndex(int cwindex)
+{
+ if(cwIndex!=cwindex) {
+ PRINTF(L_CORE_PIDS,"%s: shifting cwIndex from %d to %d",id,cwIndex,cwindex);
+ free(id);
+ asprintf(&id,"%d.%d",cardNum,cwindex);
+ dataMutex.Lock();
+ trigger=true;
+ cwIndex=cwindex;
+ for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
+ cam->SetCWIndex(pid->Pid(),cwIndex);
+ dataMutex.Unlock();
+ if(filter) filter->Wakeup();
+ }
+}
+
+void cEcmHandler::SetPrg(cPrg *prg)
+{
+ dataMutex.Lock();
+ bool wasIdle=IsIdle();
+ if(prg->Prg()!=sid) {
+ PRINTF(L_CORE_ECM,"%s: setting new SID %d",id,prg->Prg());
+ sid=prg->Prg();
+ idleTime.Set();
+ pids.Clear();
+ trigger=true;
+ }
+ LBSTART(L_CORE_PIDS);
+ LBPUT("%s: pids on entry",id);
+ for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
+ LBPUT(" %s=%04x",TYPENAME(pid->Type()),pid->Pid());
+ LBEND();
+
+ for(cPrgPid *pid=pids.First(); pid;) {
+ cPrgPid *npid;
+ for(npid=prg->pids.First(); npid; npid=prg->pids.Next(npid)) {
+ if(pid->Pid()==npid->Pid()) {
+ npid->Proc(true);
+ break;
+ }
+ }
+ if(!npid) {
+ npid=pids.Next(pid);
+ pids.Del(pid);
+ pid=npid;
+ }
+ else pid=pids.Next(pid);
+ }
+ LBSTART(L_CORE_PIDS);
+ LBPUT("%s: pids after delete",id);
+ for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
+ LBPUT(" %s=%04x",TYPENAME(pid->Type()),pid->Pid());
+ LBEND();
+ for(cPrgPid *npid=prg->pids.First(); npid; npid=prg->pids.Next(npid)) {
+ if(!npid->Proc()) {
+ cPrgPid *pid=new cPrgPid(npid->Type(),npid->Pid());
+ pids.Add(pid);
+ cam->SetCWIndex(pid->Pid(),cwIndex);
+ }
+ }
+ LBSTART(L_CORE_PIDS);
+ LBPUT("%s: pids after add",id);
+ for(cPrgPid *pid=pids.First(); pid; pid=pids.Next(pid))
+ LBPUT(" %s=%04x",TYPENAME(pid->Type()),pid->Pid());
+ LBEND();
+ if(!IsIdle()) {
+ trigger=true;
+ triggerMode=0;
+ if(wasIdle) PRINTF(L_CORE_ECM,"%s: is no longer idle",id);
+ }
+ else {
+ if(!wasIdle) idleTime.Set();
+ PRINTF(L_CORE_ECM,"%s: is idle%s",id,wasIdle?"":" now");
+ }
+
+ if(!filter) {
+ filter=NewFilter(IDLE_SLEEP);
+ if(!filter) PRINTF(L_GEN_ERROR,"failed to open ECM filter in handler %s",id);
+ }
+ dataMutex.Unlock();
+ if(filter) filter->Wakeup();
+}
+
+void cEcmHandler::Process(cPidFilter *filter, unsigned char *data, int len)
+{
+ dataMutex.Lock();
+ if(trigger) {
+ PRINTF(L_CORE_ECM,"%s: triggered SID %d/%d idx %d/%d mode %d/%d %s",
+ id,filterSid,sid,filterCwIndex,cwIndex,mode,triggerMode,(mode==3 && sync)?"sync":"-");
+ trigger=false;
+ if(filterSid!=sid) {
+ filterSid=sid;
+ filterSource=cam->Source();
+ filterTransponder=cam->Transponder();
+ filterCwIndex=cwIndex;
+ noKey=true; mode=0;
+ }
+ else {
+ if(filterCwIndex!=cwIndex) {
+ filterCwIndex=cwIndex;
+ if(mode==3 && sync)
+ cam->WriteCW(filterCwIndex,lastCw,true);
+ }
+ if(mode<triggerMode) mode=triggerMode;
+ }
+ triggerMode=-1;
+ }
+ dataMutex.Unlock();
+
+ switch(mode) {
+ case -1:
+ filter->SetIdleTime(IDLE_SLEEP);
+ break;
+
+ case 0:
+ StopEcm();
+ if(IsIdle()) { mode=-1; break; }
+
+ dolog=LOG_COUNT;
+ NewEcm();
+ filter->SetIdleTime(IDLE_GETCA);
+ startecm.Set();
+ mode=1;
+ break;
+
+ case 1:
+ if(!ecm && !JumpEcm()) {
+ if(startecm.Elapsed()>IDLE_GETCA_SLOW) {
+ if(IsIdle()) { mode=0; break; }
+ PRINTF(L_CORE_ECM,"%s: no encryption system found",id);
+ filter->SetIdleTime(IDLE_GETCA_SLOW/4);
+ startecm.Set();
+ }
+ break;
+ }
+ mode=4;
+ // fall through
+
+ case 4:
+ case 5:
+ NoSync(mode==4);
+ failed.Clear();
+ filter->SetIdleTime(IDLE_NO_SYNC/2);
+ lastsync.Set();
+ cryptPeriod=20*1000;
+ mode=2;
+ // fall through
+
+ case 2:
+ if(sys->NeedsData()) {
+ if(!UpdateEcm()) {
+ if(lastsync.Elapsed()<ECM_DATA_TIME) break;
+ PRINTF(L_CORE_ECM,"%s: no ecm extra data update (waited %d ms)",id,(int)lastsync.Elapsed());
+ }
+ if(lastsync.Elapsed()>IDLE_NO_SYNC/4 && dolog)
+ PRINTF(L_CORE_ECM,"%s: ecm extra data update took %d ms",id,(int)lastsync.Elapsed());
+ }
+ filter->SetIdleTime(IDLE_NO_SYNC);
+ mode=3;
+ // fall through
+
+ case 3:
+ {
+ bool resend=false, cwok=false;
+ if(resendTime.TimedOut()) {
+ resend=sync; resendTime.Set(8*24*60*60*1000);
+ }
+
+ if(startecm.Elapsed()<3*60*1000) cam->DumpAV7110();
+
+ if(data && len>0) {
+ HEXDUMP(L_HEX_ECM,data,len,"ECM sys 0x%04x id 0x%02x pid 0x%04x",ecm->caId,ecm->provId,filter->Pid());
+ if(SCT_LEN(data)==len) {
+ LDUMP(L_CORE_ECMPROC,data,16,"%s: ECM",id);
+ int n;
+ if(!(n=sys->CheckECM(ecm,data,sync))) {
+ if(resend || parity!=(data[0]&1)) {
+ int ecmid;
+ cTimeMs procTime;
+ cwok=(ecmid=failed.Get(data,len,0))>=0 && sys->ProcessECM(ecm,data);
+ n=(ecmid>0)?failed.Cache(ecmid,cwok,0):99;
+ sys->CheckECMResult(ecm,data,cwok);
+ if(cwok) {
+ parity=data[0]&1;
+ }
+ else {
+ if(procTime.Elapsed()>6000) {
+ PRINTF(L_CORE_ECM,"%s: filter flush (elapsed %d)",id,(int)procTime.Elapsed());
+ filter->Flush();
+ }
+ if(n>=2) { count++; if(n==2) count++; }
+ parity=0xFF;
+ if(sync && lastsync.Elapsed()>cryptPeriod*2) {
+ PRINTF(L_CORE_ECM,"%s: lost sync (period %d, elapsed %d)",id,cryptPeriod,(int)lastsync.Elapsed());
+ NoSync(true);
+ }
+ }
+ PRINTF(L_CORE_ECMPROC,"%s: (%s) cwok=%d ecmid=%d n=%d sync=%d parity=%d count=%d ELA=%d",
+ id,sys->Name(),cwok,ecmid,n,sync,parity,count,(int)procTime.Elapsed());
+ }
+ }
+ else {
+ PRINTF(L_CORE_ECMPROC,"%s: check result %d\n",id,n);
+ switch(n) {
+ case 1: NoSync(true); break;
+ case 2: count++; break;
+ case 3: break;
+ }
+ }
+ }
+ else {
+ PRINTF(L_CORE_ECM,"%s: incomplete section %d != %d",id,len,SCT_LEN(data));
+ count++;
+ }
+ }
+ else if(sys->Constant()) {
+ if(sys->ProcessECM(ecm,NULL)) {
+ cwok=true;
+ if(sync) filter->SetIdleTime(IDLE_SYNC*10);
+ }
+ else count++;
+ }
+ else count++;
+
+ if(cwok) {
+ dolog=LOG_COUNT; sys->DoLog(true);
+ cam->WriteCW(filterCwIndex,sys->CW(),resend || !sync);
+ memcpy(lastCw,sys->CW(),sizeof(lastCw));
+ noKey=false; count=0;
+ UpdateEcm(); EcmOk();
+ if(!sync) {
+ sync=true;
+ filter->SetIdleTime(IDLE_SYNC);
+ PRINTF(L_CORE_ECM,"%s: correct key found",id);
+ if(!cam->IsSoftCSA())
+ resendTime.Set(CW_REPEAT_TIME);
+ }
+ else if(!resend)
+ cryptPeriod=max(5000,min(60000,(int)lastsync.Elapsed()));
+ lastsync.Set();
+ }
+
+ if(!sync && !trigger) {
+ if(count>=sys->MaxEcmTry()) {
+ EcmFail(); JumpEcm();
+ mode=4;
+ if(!ecm) {
+ JumpEcm();
+ if(!ecm) { // this should not happen!
+ PRINTF(L_GEN_DEBUG,"internal: handler %s, empty ecm list in sync loop",id);
+ mode=0; break;
+ }
+ // if we looped through all systems, we wait until the next parity
+ // change before we try again.
+ if(dolog!=LOG_COUNT && data) { parity=data[0]&1; mode=5; }
+ if(dolog && !--dolog) {
+ sys->DoLog(false);
+ PRINTF(L_CORE_ECM,"%s: stopping message log until valid key is found",id);
+ }
+ }
+ break;
+ }
+ }
+
+ if(IsIdle() && idleTime.Elapsed()>MAX_ECM_HOLD) {
+ PRINTF(L_CORE_ECM,"%s: hold timeout expired",id);
+ mode=0;
+ }
+
+ break;
+ }
+ }
+}
+
+void cEcmHandler::NoSync(bool clearParity)
+{
+ if(clearParity) parity=0xFF;
+ count=0; sync=false;
+}
+
+void cEcmHandler::DeleteSys(void)
+{
+ delete sys; sys=0;
+}
+
+char *cEcmHandler::CurrentKeyStr(void) const
+{
+ if(noKey || !sys) return 0;
+ return strdup(sys->CurrentKeyStr());
+}
+
+cEcmInfo *cEcmHandler::NewEcm(void)
+{
+ ecmcache.GetCached(&ecmList,filterSid,filterSource,filterTransponder);
+ ecmPriList.Clear();
+ for(cEcmInfo *n=ecmList.First(); n; n=ecmList.Next(n)) AddEcmPri(n);
+ ecm=0; ecmPri=0;
+ return JumpEcm();
+}
+
+void cEcmHandler::AddEcmPri(cEcmInfo *n)
+{
+ int ident, pri=0;
+ while((ident=cSystems::FindIdentBySysId(n->caId,!cam->IsSoftCSA(),pri))>0) {
+ cEcmPri *ep=new cEcmPri;
+ if(ep) {
+ ep->ecm=n;
+ ep->pri=pri;
+ ep->sysIdent=ident;
+ if(n->Cached() && (!ScSetup.LocalPriority || pri!=-15)) ep->pri+=20;
+
+ // keep ecmPriList sorted
+ cEcmPri *eppp, *epp=ecmPriList.First();
+ if(!epp || epp->pri<ep->pri)
+ ecmPriList.Ins(ep);
+ else {
+ do {
+ eppp=ecmPriList.Next(epp);
+ if(!eppp || eppp->pri<ep->pri) {
+ ecmPriList.Add(ep,epp);
+ break;
+ }
+ } while((epp=eppp));
+ }
+ }
+ }
+}
+
+void cEcmHandler::StopEcm(void)
+{
+ filter->Stop(); filter->Flush();
+ if(ecm) cam->LogEcmStatus(ecm,false);
+ DeleteSys();
+}
+
+bool cEcmHandler::UpdateEcm(void)
+{
+ if(!ecm->Data() || ecmUpdTime.TimedOut()) {
+ bool log=dolog;
+ dolog=(sys && sys->NeedsData() && ecm->Data()==0);
+ if(dolog) PRINTF(L_CORE_ECM,"%s: try to update ecm extra data",id);
+ ParseCAInfo(ecm->caId);
+ ecmUpdTime.Set(ECM_UPD_TIME);
+ dolog=log;
+ if(!ecm->Data()) return false;
+ }
+ return true;
+}
+
+cEcmInfo *cEcmHandler::JumpEcm(void)
+{
+ noKey=true;
+ if(!ecmPri) {
+ ParseCAInfo(0xFFFF); // all systems
+ ecmPri=ecmPriList.First();
+ }
+ else ecmPri=ecmPriList.Next(ecmPri);
+ if(ecmPri) {
+ if(ecmPri->ecm!=ecm) {
+ StopEcm(); ecmUpdTime.Set();
+ ecm=ecmPri->ecm;
+ filter->Start(ecm->ecm_pid,ecm->ecm_table,0xfe,0,false);
+ cam->LogEcmStatus(ecm,true);
+ }
+ else {
+ DeleteSys();
+ filter->Flush();
+ }
+ sys=cSystems::FindBySysIdent(ecmPri->sysIdent);
+ if(!sys) {
+ if(dolog) PRINTF(L_GEN_DEBUG,"internal: handler %s, no system found for ident %04x (caid %04x pri %d)",id,ecmPri->sysIdent,ecmPri->ecm->caId,ecmPri->pri);
+ return JumpEcm();
+ }
+ sys->DoLog(dolog!=0); sys->CardNum(cardNum);
+ failed.SetMaxFail(sys->MaxEcmTry());
+
+ if(dolog) PRINTF(L_CORE_ECM,"%s: try system %s (%04x) id %04x with ecm %x%s (pri=%d)",
+ id,sys->Name(),ecm->caId,ecm->provId,ecm->ecm_pid,ecm->Cached()?" (cached)":"",sys->Pri());
+ }
+ else {
+ StopEcm();
+ ecm=0;
+ }
+ return ecm;
+}
+
+void cEcmHandler::EcmOk(void)
+{
+ ecm->SetName(sys->Name());
+ ecm->Fail(false);
+ ecmcache.New(ecm);
+ cEcmInfo *e=ecmList.First();
+ while(e) {
+ if(e->Cached() && e->Failed()) ecmcache.Delete(e);
+ e=ecmList.Next(e);
+ }
+ cLoaders::SaveCache();
+}
+
+void cEcmHandler::EcmFail(void)
+{
+ ecm->Fail(true);
+}
+
+void cEcmHandler::ParseCAInfo(int SysId)
+{
+ unsigned char buff[2048];
+ caid_t casys[MAXCAIDS+1];
+ if(SysId==0xFFFF) {
+ if(!cam->GetPrgCaids(filterSource,filterTransponder,filterSid,casys)) {
+ PRINTF(L_CORE_ECM,"%s: no CAIDs for SID %d",id,sid);
+ return;
+ }
+ }
+ else {
+ casys[0]=SysId;
+ casys[1]=0;
+ }
+ bool streamFlag;
+ int len=GetCaDescriptors(filterSource,filterTransponder,filterSid,casys,sizeof(buff),buff,streamFlag);
+ if(len>0) {
+ if(dolog) PRINTF(L_CORE_ECM,"%s: got CaDescriptors for SID %d (len=%d)",id,sid,len);
+ HEXDUMP(L_HEX_PMT,buff,len,"PMT");
+ for(int index=0; index<len; index+=buff[index+1]+2) {
+ if(buff[index]==0x09) {
+ if(dolog) LDUMP(L_CORE_ECM,&buff[index+2],buff[index+1],"%s: descriptor",id);
+ int sysId=WORD(buff,index+2,0xFFFF);
+ int sysPri=0;
+ cSystem *sys;
+ while((sys=cSystems::FindBySysId(sysId,!cam->IsSoftCSA(),sysPri))) {
+ sysPri=sys->Pri();
+ cSimpleList<cEcmInfo> ecms;
+ sys->ParseCADescriptor(&ecms,sysId,&buff[index+2],buff[index+1]);
+ delete sys;
+ if(ecms.Count()) {
+ cEcmInfo *n;
+ while((n=ecms.First())) {
+ ecms.Del(n,false);
+ LBSTARTF(L_CORE_ECM);
+ if(dolog) LBPUT("%s: found %04x (%s) id %04x with ecm %x ",id,n->caId,n->name,n->provId,n->ecm_pid);
+ cEcmInfo *e=ecmList.First();
+ while(e) {
+ if(e->ecm_pid==n->ecm_pid) {
+ if(e->caId==n->caId && e->provId==n->provId) {
+ if(n->Data()) {
+ if(e->Update(n) && dolog) LBPUT("(updated) ");
+ }
+ if(dolog) LBPUT("(already present)");
+ delete n; n=0;
+ break;
+ }
+ else {
+ e->Fail(true);
+ if(dolog) LBPUT("(dup) ");
+ }
+ }
+ e=ecmList.Next(e);
+ }
+ if(n) {
+ if(dolog) LBPUT("(new)");
+ n->SetSource(sid,filterSource,filterTransponder);
+ ecmList.Add(n);
+ AddEcmPri(n);
+ }
+ LBEND();
+ }
+ break;
+ }
+ }
+ if(sysPri==0 && dolog) PRINTF(L_CORE_ECM,"%s: no module available for system %04x",id,sysId);
+ }
+ }
+ }
+ else if(len<0)
+ PRINTF(L_CORE_ECM,"%s: CA parse buffer overflow",id);
+ if(SysId==0xFFFF) {
+ for(cEcmPri *ep=ecmPriList.First(); ep; ep=ecmPriList.Next(ep))
+ PRINTF(L_CORE_ECMPROC,"%s: ecmPriList pri=%d ident=%04x caid=%04x pid=%04x",id,ep->pri,ep->sysIdent,ep->ecm->caId,ep->ecm->ecm_pid);
+ PRINTF(L_CORE_ECMPROC,"%s: ecmPri list end",id);
+ }
+}
+
+// -- cCam ---------------------------------------------------------------
+
+cCam::cCam(cScDvbDevice *dev, int CardNum)
+{
+ device=dev; cardNum=CardNum;
+ source=transponder=-1; liveVpid=liveApid=0; logger=0; hookman=0;
+ memset(lastCW,0,sizeof(lastCW));
+ memset(indexMap,0,sizeof(indexMap));
+}
+
+cCam::~cCam()
+{
+ handlerList.Clear();
+ delete hookman;
+ delete logger;
+}
+
+bool cCam::IsSoftCSA(void)
+{
+ return device->SoftCSA();
+}
+
+void cCam::Tune(const cChannel *channel)
+{
+ cMutexLock lock(this);
+ if(source!=channel->Source() || transponder!=channel->Transponder()) {
+ source=channel->Source(); transponder=channel->Transponder();
+ PRINTF(L_CORE_PIDS,"%d: now tuned to source %x transponder %x",cardNum,source,transponder);
+ Stop();
+ }
+ else PRINTF(L_CORE_PIDS,"%d: tune to same source/transponder",cardNum);
+}
+
+void cCam::SetPid(int type, int pid, bool on)
+{
+ cMutexLock lock(this);
+ int oldA=liveApid, oldV=liveVpid;
+ if(type==1) liveVpid=on ? pid:0;
+ else if(type==0) liveApid=on ? pid:0;
+ else if(liveVpid==pid && on) liveVpid=0;
+ else if(liveApid==pid && on) liveApid=0;
+ if(oldA!=liveApid || oldV!=liveVpid)
+ PRINTF(L_CORE_PIDS,"%d: livepids video=%04x audio=%04x",cardNum,liveVpid,liveApid);
+}
+
+void cCam::Stop(void)
+{
+ cMutexLock lock(this);
+ for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler))
+ handler->Stop();
+ if(logger) logger->Down();
+ if(hookman) hookman->Down();
+}
+
+void cCam::AddPrg(cPrg *prg)
+{
+ cMutexLock lock(this);
+ bool islive=false;
+ for(cPrgPid *pid=prg->pids.First(); pid; pid=prg->pids.Next(pid))
+ if(pid->Pid()==liveVpid || pid->Pid()==liveApid) {
+ islive=true;
+ break;
+ }
+ bool needZero=!IsSoftCSA() && (islive || !ScSetup.ConcurrentFF);
+ bool noshift=IsSoftCSA() || (prg->IsUpdate() && prg->pids.Count()==0);
+ PRINTF(L_CORE_PIDS,"%d: %s SID %d (zero=%d noshift=%d)",cardNum,prg->IsUpdate()?"update":"add",prg->Prg(),needZero,noshift);
+ if(prg->pids.Count()>0) {
+ LBSTART(L_CORE_PIDS);
+ LBPUT("%d: pids",cardNum);
+ for(cPrgPid *pid=prg->pids.First(); pid; pid=prg->pids.Next(pid))
+ LBPUT(" %s=%04x",TYPENAME(pid->Type()),pid->Pid());
+ LBEND();
+ }
+ cEcmHandler *handler=GetHandler(prg->Prg(),needZero,noshift);
+ if(handler) {
+ PRINTF(L_CORE_PIDS,"%d: found handler for SID %d (%s idle=%d idx=%d)",cardNum,prg->Prg(),handler->Id(),handler->IsIdle(),handler->CwIndex());
+ handler->SetPrg(prg);
+ }
+}
+
+bool cCam::HasPrg(int prg)
+{
+ cMutexLock lock(this);
+ for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler))
+ if(!handler->IsIdle() && handler->Sid()==prg)
+ return true;
+ return false;
+}
+
+char *cCam::CurrentKeyStr(int num)
+{
+ cMutexLock lock(this);
+ cEcmHandler *handler;
+ for(handler=handlerList.First(); handler; handler=handlerList.Next(handler))
+ if(--num<0) return handler->CurrentKeyStr();
+ return 0;
+}
+
+bool cCam::Active(void)
+{
+ cMutexLock lock(this);
+ for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler))
+ if(!handler->IsIdle()) {
+ PRINTF(L_GEN_INFO,"handler %s on card %d is not idle",handler->Id(),cardNum);
+ return true;
+ }
+ return false;
+}
+
+void cCam::HouseKeeping(void)
+{
+ cMutexLock lock(this);
+ for(cEcmHandler *handler=handlerList.First(); handler;) {
+ cEcmHandler *next=handlerList.Next(handler);
+ if(handler->IsRemoveable()) RemHandler(handler);
+ handler=next;
+ }
+ if(handlerList.Count()<1) {
+ delete hookman; hookman=0;
+ delete logger; logger=0;
+ }
+}
+
+bool cCam::GetPrgCaids(int source, int transponder, int prg, caid_t *c)
+{
+ return device->GetPrgCaids(source,transponder,prg,c);
+}
+
+void cCam::LogEcmStatus(const cEcmInfo *ecm, bool on)
+{
+ cMutexLock lock(this);
+ if(!logger && on && ScSetup.AutoUpdate) {
+ logger=new cLogger(cardNum,IsSoftCSA());
+ LogStatsUp();
+ }
+ if(logger) logger->EcmStatus(ecm,on);
+}
+
+void cCam::AddHook(cLogHook *hook)
+{
+ cMutexLock lock(this);
+ if(!hookman) hookman=new cHookManager(cardNum);
+ if(hookman) hookman->AddHook(hook);
+}
+
+bool cCam::TriggerHook(int id)
+{
+ return hookman && hookman->TriggerHook(id);
+}
+
+void cCam::SetCWIndex(int pid, int index)
+{
+ if(index<MAX_CW_IDX) {
+ ca_pid_t ca_pid;
+ ca_pid.pid=pid;
+ ca_pid.index=index;
+ PRINTF(L_CORE_PIDS,"%d: descrambling pid %04x on index %x",cardNum,pid,index);
+ if(!device->SetCaPid(&ca_pid))
+ if(index>0) {
+ PRINTF(L_GEN_ERROR,"CA_SET_PID failed (%s). Expect a black screen/bad recording. Do you use the patched DVB driver?",strerror(errno));
+ PRINTF(L_GEN_WARN,"Adjusting 'Concurrent FF streams' to NO");
+ ScSetup.ConcurrentFF=0;
+ ScSetup.Store(true);
+ }
+ }
+}
+
+void cCam::WriteCW(int index, unsigned char *cw, bool force)
+{
+ if(index<MAX_CW_IDX) {
+ for(int i=0; i<16; i+=4) cw[i+3]=cw[i]+cw[i+1]+cw[i+2];
+ ca_descr_t ca_descr;
+ ca_descr.index=index;
+ unsigned char *last=lastCW[index];
+ if(force || memcmp(&cw[0],&last[0],8)) {
+ memcpy(&last[0],&cw[0],8);
+ ca_descr.parity=0;
+ memcpy(ca_descr.cw,&cw[0],8);
+ if(!device->SetCaDescr(&ca_descr))
+ PRINTF(L_GEN_ERROR,"CA_SET_DESCR failed (%s). Expect a black screen.",strerror(errno));
+ }
+
+ if(force || memcmp(&cw[8],&last[8],8)) {
+ memcpy(&last[8],&cw[8],8);
+ ca_descr.parity=1;
+ memcpy(ca_descr.cw,&cw[8],8);
+ if(!device->SetCaDescr(&ca_descr))
+ PRINTF(L_GEN_ERROR,"CA_SET_DESCR failed (%s). Expect a black screen.",strerror(errno));
+ }
+ }
+}
+
+void cCam::DumpAV7110(void)
+{
+ device->DumpAV7110();
+}
+
+int cCam::GetFreeIndex(void)
+{
+ for(int idx=0; idx<MAX_CW_IDX; idx++)
+ if(!indexMap[idx]) return idx;
+ return -1;
+}
+
+cEcmHandler *cCam::GetHandler(int sid, bool needZero, bool noshift)
+{
+ cEcmHandler *zerohandler=0, *sidhandler=0, *idlehandler=0;
+ for(cEcmHandler *handler=handlerList.First(); handler; handler=handlerList.Next(handler)) {
+ if(handler->Sid()==sid)
+ sidhandler=handler;
+ if(handler->CwIndex()==0)
+ zerohandler=handler;
+ if(handler->IsIdle() && (!idlehandler || (!(needZero ^ (idlehandler->CwIndex()!=0)) && (needZero ^ (handler->CwIndex()!=0))) ))
+ idlehandler=handler;
+ }
+ LBSTART(L_CORE_PIDS);
+ LBPUT("%d: SID=%d zero=%d |",cardNum,sid,needZero);
+ if(sidhandler) LBPUT(" sid=%d/%d/%d",sidhandler->CwIndex(),sidhandler->Sid(),sidhandler->IsIdle());
+ else LBPUT(" sid=-/-/-");
+ if(zerohandler) LBPUT(" zero=%d/%d/%d",zerohandler->CwIndex(),zerohandler->Sid(),zerohandler->IsIdle());
+ else LBPUT(" zero=-/-/-");
+ if(idlehandler) LBPUT(" idle=%d/%d/%d",idlehandler->CwIndex(),idlehandler->Sid(),idlehandler->IsIdle());
+ else LBPUT(" idle=-/-/-");
+ LBEND();
+
+ if(sidhandler) {
+ if(needZero && sidhandler->CwIndex()!=0 && !noshift) {
+ if(!sidhandler->IsIdle())
+ PRINTF(L_CORE_ECM,"%d: shifting cwindex on non-idle handler.",cardNum);
+ if(zerohandler) {
+ if(!zerohandler->IsIdle())
+ PRINTF(L_CORE_ECM,"%d: shifting non-idle zero handler. This shouldn't happen!",cardNum);
+ zerohandler->ShiftCwIndex(sidhandler->CwIndex());
+ sidhandler->ShiftCwIndex(0);
+ }
+ else if(indexMap[0]==0) {
+ indexMap[0]=1;
+ indexMap[sidhandler->CwIndex()]=0;
+ sidhandler->ShiftCwIndex(0);
+ }
+ else PRINTF(L_CORE_ECM,"%d: zero index not free.",cardNum);
+ }
+
+ if(!needZero && sidhandler->CwIndex()==0 && !noshift) {
+ if(!sidhandler->IsIdle())
+ PRINTF(L_CORE_ECM,"%d: shifting cwindex on non-idle handler.",cardNum);
+ int idx=GetFreeIndex();
+ if(idx>=0) {
+ indexMap[idx]=1;
+ sidhandler->ShiftCwIndex(idx);
+ indexMap[0]=0;
+ }
+ else PRINTF(L_CORE_ECM,"%d: no free cwindex. Can't free zero index.",cardNum);
+ }
+
+ return sidhandler;
+ }
+
+ if(needZero && zerohandler) {
+ if(!zerohandler->IsIdle())
+ PRINTF(L_CORE_ECM,"%d: changing SID on non-idle zero handler. This shouldn't happen!",cardNum);
+ return zerohandler;
+ }
+
+ if(idlehandler) {
+ if(needZero && idlehandler->CwIndex()!=0 && !noshift) {
+ if(indexMap[0]==0) {
+ indexMap[0]=1;
+ indexMap[idlehandler->CwIndex()]=0;
+ idlehandler->ShiftCwIndex(0);
+ }
+ else PRINTF(L_CORE_ECM,"%d: zero index not free. (2)",cardNum);
+ }
+ if(!needZero && idlehandler->CwIndex()==0 && !noshift) {
+ int idx=GetFreeIndex();
+ if(idx>=0) {
+ indexMap[idx]=1;
+ idlehandler->ShiftCwIndex(idx);
+ indexMap[0]=0;
+ }
+ else PRINTF(L_CORE_ECM,"%d: no free cwindex. Can't free zero index. (2)",cardNum);
+ }
+ if((needZero ^ (idlehandler->CwIndex()==0)))
+ PRINTF(L_CORE_ECM,"%d: idlehandler index doesn't match needZero",cardNum);
+ return idlehandler;
+ }
+
+ int idx=GetFreeIndex();
+ if(!needZero && idx==0) {
+ indexMap[0]=1;
+ idx=GetFreeIndex();
+ indexMap[0]=0;
+ if(idx<0) {
+ idx=0;
+ PRINTF(L_CORE_ECM,"%d: can't respect !needZero for new handler",cardNum);
+ }
+ }
+ if(idx<0) {
+ PRINTF(L_CORE_ECM,"%d: no free cwindex",cardNum);
+ return 0;
+ }
+ indexMap[idx]=1;
+ idlehandler=new cEcmHandler(this,cardNum,idx);
+ handlerList.Add(idlehandler);
+ return idlehandler;
+}
+
+void cCam::RemHandler(cEcmHandler *handler)
+{
+ int idx=handler->CwIndex();
+ PRINTF(L_CORE_PIDS,"%d: removing %s on cw index %d",cardNum,handler->Id(),idx);
+ handlerList.Del(handler);
+ indexMap[idx]=0;
+}
+
+// --- cChannelCaids -----------------------------------------------------------
+
+#if APIVERSNUM >= 10500
+
+class cChannelCaids : public cSimpleItem {
+private:
+ int prg, source, transponder;
+ int numcaids;
+ caid_t caids[MAX_CI_SLOT_CAIDS+1];
+public:
+ cChannelCaids(cChannel *channel);
+ bool IsChannel(cChannel *channel);
+ void Sort(void);
+ void Del(caid_t caid);
+ bool HasCaid(caid_t caid);
+ bool Same(cChannelCaids *ch);
+ void HistAdd(unsigned short *hist);
+ void Dump(int n);
+ const caid_t *Caids(void) { caids[numcaids]=0; return caids; }
+ int NumCaids(void) { return numcaids; }
+ };
+
+cChannelCaids::cChannelCaids(cChannel *channel)
+{
+ prg=channel->Sid(); source=channel->Source(); transponder=channel->Transponder();
+ numcaids=0;
+ for(const caid_t *ids=channel->Caids(); *ids; ids++)
+ if(numcaids<MAX_CI_SLOT_CAIDS) caids[numcaids++]=*ids;
+ Sort();
+}
+
+bool cChannelCaids::IsChannel(cChannel *channel)
+{
+ return prg==channel->Sid() && source==channel->Source() && transponder==channel->Transponder();
+}
+
+void cChannelCaids::Sort(void)
+{
+ caid_t tmp[MAX_CI_SLOT_CAIDS];
+ int c=0xFFFF;
+ for(int i=0; i<numcaids; i++) {
+ int d=0;
+ for(int j=0; j<numcaids; j++) if(caids[j]>d && caids[j]<c) d=caids[j];
+ tmp[i]=d; c=d;
+ }
+ memcpy(caids,tmp,sizeof(caids));
+}
+
+void cChannelCaids::Del(caid_t caid)
+{
+ for(int i=0; i<numcaids; i++)
+ if(caids[i]==caid) {
+ numcaids--; caids[i]=caids[numcaids];
+ if(numcaids>0) Sort();
+ caids[numcaids]=0;
+ break;
+ }
+}
+
+bool cChannelCaids::HasCaid(caid_t caid)
+{
+ for(int i=0; i<numcaids; i++) if(caids[i]==caid) return true;
+ return false;
+}
+
+bool cChannelCaids::Same(cChannelCaids *ch)
+{
+ if(numcaids!=ch->numcaids) return false;
+ return memcmp(caids,ch->caids,numcaids*sizeof(caid_t))==0;
+}
+
+void cChannelCaids::HistAdd(unsigned short *hist)
+{
+ for(int i=numcaids-1; i>=0; i--) hist[caids[i]]++;
+}
+
+void cChannelCaids::Dump(int n)
+{
+ LBSTART(L_CORE_CAIDS);
+ LBPUT("%d: channel %d/%x/%x",n,prg,source,transponder);
+ for(const caid_t *ids=Caids(); *ids; ids++) LBPUT(" %04x",*ids);
+ LBEND();
+}
+
+// --- cChannelList ------------------------------------------------------------
+
+class cChannelList : public cSimpleList<cChannelCaids> {
+private:
+ int n;
+public:
+ cChannelList(int N);
+ void Unique(void);
+ void CheckIgnore(void);
+ int Histo(void);
+ void Purge(int caid);
+ };
+
+cChannelList::cChannelList(int N)
+{
+ n=N;
+}
+
+void cChannelList::CheckIgnore(void)
+{
+ for(cChannelCaids *ch=First(); ch; ch=Next(ch)) {
+ const caid_t *ids=ch->Caids();
+ while(*ids) {
+ int pri=0;
+ if(!cSystems::FindIdentBySysId(*ids,false,pri)) {
+ for(cChannelCaids *ch2=Next(ch); ch2; ch2=Next(ch2)) ch2->Del(*ids);
+ ch->Del(*ids);
+ }
+ else ids++;
+ }
+ }
+ PRINTF(L_CORE_CAIDS,"%d: after check",n);
+ for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->Dump(n);
+}
+
+void cChannelList::Unique(void)
+{
+ for(cChannelCaids *ch1=First(); ch1; ch1=Next(ch1)) {
+ for(cChannelCaids *ch2=Next(ch1); ch2;) {
+ if(ch1->Same(ch2) || ch2->NumCaids()<1) {
+ cChannelCaids *t=Next(ch2);
+ Del(ch2);
+ ch2=t;
+ }
+ else ch2=Next(ch2);
+ }
+ }
+ if(Count()==1 && First() && First()->NumCaids()<1) Del(First());
+ PRINTF(L_CORE_CAIDS,"%d: after unique",n);
+ for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->Dump(n);
+}
+
+int cChannelList::Histo(void)
+{
+ int h=-1;
+ unsigned short *hist=MALLOC(unsigned short,0x10000);
+ if(hist) {
+ memset(hist,0,sizeof(unsigned short)*0x10000);
+ for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->HistAdd(hist);
+ int c=0;
+ for(int i=0; i<0x10000; i++)
+ if(hist[i]>c) { h=i; c=hist[i]; }
+ free(hist);
+ }
+ else PRINTF(L_GEN_ERROR,"malloc failed in cChannelList::Histo");
+ return h;
+}
+
+void cChannelList::Purge(int caid)
+{
+ for(cChannelCaids *ch=First(); ch;) {
+ if(ch->NumCaids()<=0 || ch->HasCaid(caid)) {
+ cChannelCaids *t=Next(ch);
+ Del(ch);
+ ch=t;
+ }
+ else ch=Next(ch);
+ }
+ if(Count()>0) {
+ PRINTF(L_CORE_CAIDS,"%d: still left",n);
+ for(cChannelCaids *ch=First(); ch; ch=Next(ch)) ch->Dump(n);
+ }
+}
+
+// -- cScCiAdapter -------------------------------------------------------------
+
+struct TPDU {
+ unsigned char slot;
+ unsigned char tcid;
+ unsigned char tag;
+ unsigned char len;
+ unsigned char data[1];
+ };
+
+class cScCamSlot;
+
+class cScCiAdapter : public cCiAdapter {
+private:
+ cDevice *device;
+ cCam *cam;
+ cMutex ciMutex;
+ int cardIndex;
+ cRingBufferLinear *rb;
+ cScCamSlot *slots[MAX_CI_SLOTS];
+ //
+ cTimeMs caidTimer;
+ int version[MAX_CI_SLOTS];
+ caid_t caids[MAX_CI_SLOTS][MAX_CI_SLOT_CAIDS+1];
+ int tcid;
+ //
+ cTimeMs readTimer, writeTimer;
+ //
+ void BuildCaids(bool force);
+protected:
+ virtual int Read(unsigned char *Buffer, int MaxLength);
+ virtual void Write(const unsigned char *Buffer, int Length);
+ virtual bool Reset(int Slot);
+ virtual eModuleStatus ModuleStatus(int Slot);
+ virtual bool Assign(cDevice *Device, bool Query=false);
+public:
+ cScCiAdapter(cDevice *Device, int CardIndex, cCam *Cam);
+ ~cScCiAdapter();
+ void CamStop(void);
+ void CamAddPrg(cPrg *prg);
+ bool CamSoftCSA(void);
+ int GetCaids(int slot, unsigned short *Caids, int max);
+ };
+
+// -- cScCamSlot ---------------------------------------------------------------
+
+#define SLOT_CAID_CHECK 10000
+#define SLOT_RESET_TIME 600
+
+class cScCamSlot : public cCamSlot, public cRingBufferLinear {
+private:
+ cScCiAdapter *ciadapter;
+ unsigned short caids[MAX_CI_SLOT_CAIDS+1];
+ int slot, cardIndex, version;
+ cTimeMs checkTimer;
+ bool reset, doReply;
+ cTimeMs resetTimer;
+ eModuleStatus lastStatus;
+ //
+ int GetLength(const unsigned char * &data);
+ void CaInfo(unsigned char *b, int tcid, int cid);
+ bool Check(void);
+public:
+ cScCamSlot(cScCiAdapter *ca, int CardIndex, int Slot);
+ void Process(const unsigned char *data, int len);
+ eModuleStatus Status(void);
+ bool Reset(bool log=true);
+ };
+
+cScCamSlot::cScCamSlot(cScCiAdapter *ca, int CardIndex, int Slot)
+:cCamSlot(ca)
+,cRingBufferLinear(KILOBYTE(2),5+1,false,"SC-CI slot answer")
+,checkTimer(-SLOT_CAID_CHECK-1000)
+{
+ ciadapter=ca; cardIndex=CardIndex; slot=Slot;
+ version=0; caids[0]=0; doReply=false; lastStatus=msReset;
+ Reset(false);
+}
+
+eModuleStatus cScCamSlot::Status(void)
+{
+ eModuleStatus status;
+ if(reset) {
+ status=msReset;
+ if(resetTimer.TimedOut()) reset=false;
+ }
+ else if(caids[0]) status=msReady;
+ else {
+ status=msPresent; //msNone;
+ Check();
+ }
+ if(status!=lastStatus) {
+ static const char *stext[] = { "none","reset","present","ready" };
+ PRINTF(L_CORE_CI,"%d.%d: status '%s'",cardIndex,slot,stext[status]);
+ lastStatus=status;
+ }
+ return status;
+}
+
+bool cScCamSlot::Reset(bool log)
+{
+ reset=true; resetTimer.Set(SLOT_RESET_TIME);
+ Clear();
+ if(log) PRINTF(L_CORE_CI,"%d.%d: reset",cardIndex,slot);
+ return true;
+}
+
+bool cScCamSlot::Check(void)
+{
+ bool res=false;
+ bool dr=ciadapter->CamSoftCSA() || ScSetup.ConcurrentFF>0;
+ if(dr!=doReply && !IsDecrypting()) {
+ PRINTF(L_CORE_CI,"%d.%d: doReply changed, reset triggered",cardIndex,slot);
+ Reset(false);
+ doReply=dr;
+ }
+ if(checkTimer.TimedOut()) {
+ if(version!=ciadapter->GetCaids(slot,0,0)) {
+ version=ciadapter->GetCaids(slot,caids,MAX_CI_SLOT_CAIDS);
+ PRINTF(L_CORE_CI,"%d.%d: now using CAIDs version %d",cardIndex,slot,version);
+ res=true;
+ }
+ checkTimer.Set(SLOT_CAID_CHECK);
+ }
+ return res;
+}
+
+int cScCamSlot::GetLength(const unsigned char * &data)
+{
+ int len=*data++;
+ if(len&0x80) {
+ int i;
+ for(i=len&~0x80, len=0; i>0; i--) len=(len<<8) + *data++;
+ }
+ return len;
+}
+
+void cScCamSlot::CaInfo(unsigned char *b, int tcid, int cid)
+{
+ b[0]=0xa0; b[2]=tcid;
+ b[3]=0x90;
+ b[4]=0x02; b[5]=cid<<8; b[6]=cid&0xff;
+ b[7]=0x9f; b[8]=0x80; b[9]=0x31; // AOT_CA_INFO
+ int l=0;
+ for(int i=0; caids[i]; i++) {
+ b[l+11]=caids[i]>>8;
+ b[l+12]=caids[i]&0xff;
+ l+=2;
+ }
+ b[10]=l; b[1]=l+9; b[-1]=l+11; Put(b-1,l+12);
+ PRINTF(L_CORE_CI,"%d.%d sending CA info",cardIndex,slot);
+}
+
+void cScCamSlot::Process(const unsigned char *data, int len)
+{
+ const unsigned char *save=data;
+ data+=3;
+ int dlen=GetLength(data);
+ if(dlen>len-(data-save)) {
+ PRINTF(L_CORE_CI,"%d.%d TDPU length exceeds data length",cardIndex,slot);
+ dlen=len-(data-save);
+ }
+ int tcid=data[0];
+
+ unsigned char a[128], *b=&a[1];
+ if(Check()) CaInfo(b,tcid,0x01);
+
+ if(dlen<8 || data[1]!=0x90) return;
+ int cid=(data[3]<<8)+data[4];
+ int tag=(data[5]<<16)+(data[6]<<8)+data[7];
+ data+=8;
+ dlen=GetLength(data);
+ if(dlen>len-(data-save)) {
+ PRINTF(L_CORE_CI,"%d.%d tag length exceeds data length",cardIndex,slot);
+ dlen=len-(data-save);
+ }
+ switch(tag) {
+ case 0x9f8030: // AOT_CA_INFO_ENQ
+ CaInfo(b,tcid,cid);
+ break;
+
+ case 0x9f8032: // AOT_CA_PMT
+ if(dlen>=6) {
+ int ca_lm=data[0];
+ int ci_cmd=-1;
+ cPrg *prg=new cPrg((data[1]<<8)+data[2],ca_lm==5);
+ int ilen=(data[4]<<8)+data[5];
+ LBSTARTF(L_CORE_CI);
+ LBPUT("%d.%d CA_PMT decoding len=%x lm=%x prg=%d len=%x",cardIndex,slot,dlen,ca_lm,(data[1]<<8)+data[2],ilen);
+ data+=6; dlen-=6;
+ LBPUT("/%x",dlen);
+ if(ilen>0 && dlen>=ilen) {
+ ci_cmd=data[0];
+ LBPUT(" ci_cmd(G)=%02x",ci_cmd);
+ }
+ data+=ilen; dlen-=ilen;
+ while(dlen>=5) {
+ cPrgPid *pid=new cPrgPid(data[0],(data[1]<<8)+data[2]);
+ prg->pids.Add(pid);
+ ilen=(data[3]<<8)+data[4];
+ LBPUT(" pid=%d,%x len=%x",data[0],(data[1]<<8)+data[2],ilen);
+ data+=5; dlen-=5;
+ LBPUT("/%x",dlen);
+ if(ilen>0 && dlen>=ilen) {
+ ci_cmd=data[0];
+ LBPUT(" ci_cmd(S)=%x",ci_cmd);
+ }
+ data+=ilen; dlen-=ilen;
+ }
+ LBEND();
+ PRINTF(L_CORE_CI,"%d.%d got CA pmt ciCmd=%d caLm=%d",cardIndex,slot,ci_cmd,ca_lm);
+ if(doReply && (ci_cmd==0x03 || (ci_cmd==0x01 && ca_lm==0x03))) {
+ b[0]=0xa0; b[2]=tcid;
+ b[3]=0x90;
+ b[4]=0x02; b[5]=cid<<8; b[6]=cid&0xff;
+ b[7]=0x9f; b[8]=0x80; b[9]=0x33; // AOT_CA_PMT_REPLY
+ b[11]=prg->Prg()<<8;
+ b[12]=prg->Prg()&0xff;
+ b[13]=0x00;
+ b[14]=0x81; // CA_ENABLE
+ b[10]=4; b[1]=4+9; a[0]=4+11; Put(a,4+12);
+ PRINTF(L_CORE_CI,"%d.%d answer to query",cardIndex,slot);
+ }
+ if(prg->Prg()!=0) {
+ if(ci_cmd==0x04) {
+ PRINTF(L_CORE_CI,"%d.%d stop decrypt",cardIndex,slot);
+ ciadapter->CamStop();
+ }
+ if(ci_cmd==0x01 || (ci_cmd==-1 && (ca_lm==0x04 || ca_lm==0x05))) {
+ PRINTF(L_CORE_CI,"%d.%d set CAM decrypt (prg %d)",cardIndex,slot,prg->Prg());
+ ciadapter->CamAddPrg(prg);
+ }
+ }
+ delete prg;
+ }
+ break;
+ }
+}
+
+// -- cScCiAdapter -------------------------------------------------------------
+
+cScCiAdapter::cScCiAdapter(cDevice *Device, int CardIndex, cCam *Cam)
+{
+ device=Device; cardIndex=CardIndex; cam=Cam;
+ tcid=0;
+ memset(version,0,sizeof(version));
+ memset(slots,0,sizeof(slots));
+ SetDescription("SC-CI adapter on device %d",cardIndex);
+ rb=new cRingBufferLinear(KILOBYTE(5),6+1,false,"SC-CI adapter read");
+ if(rb) {
+ rb->SetTimeouts(0,CAM_READ_TIMEOUT);
+/*
+ bool spare=true;
+ for(int i=0; i<MAX_CI_SLOTS; i++) {
+ if(GetCaids(i,0,0)<=0) {
+ if(!spare) break;
+ spare=false;
+ }
+ slots[i]=new cScCamSlot(this,cardIndex,i);
+ }
+*/
+ BuildCaids(true);
+ slots[0]=new cScCamSlot(this,cardIndex,0);
+ Start();
+ }
+ else PRINTF(L_GEN_ERROR,"failed to create ringbuffer for SC-CI adapter %d.",cardIndex);
+}
+
+cScCiAdapter::~cScCiAdapter()
+{
+ Cancel(3);
+ ciMutex.Lock();
+ delete rb; rb=0;
+ ciMutex.Unlock();
+}
+
+void cScCiAdapter::CamStop(void)
+{
+ if(cam) cam->Stop();
+}
+
+void cScCiAdapter::CamAddPrg(cPrg *prg)
+{
+ if(cam) cam->AddPrg(prg);
+}
+
+bool cScCiAdapter::CamSoftCSA(void)
+{
+ return cam && cam->IsSoftCSA();
+}
+
+int cScCiAdapter::GetCaids(int slot, unsigned short *Caids, int max)
+{
+ BuildCaids(false);
+ cMutexLock lock(&ciMutex);
+ if(Caids) {
+ int i;
+ for(i=0; i<MAX_CI_SLOT_CAIDS && i<max && caids[i]; i++) Caids[i]=caids[slot][i];
+ Caids[i]=0;
+ }
+ return version[slot];
+}
+
+void cScCiAdapter::BuildCaids(bool force)
+{
+ if(caidTimer.TimedOut() || force) {
+ PRINTF(L_CORE_CAIDS,"%d: building caid lists",cardIndex);
+ cChannelList list(cardIndex);
+ Channels.Lock(false);
+ for(cChannel *channel=Channels.First(); channel; channel=Channels.Next(channel)) {
+ if(!channel->GroupSep() && channel->Ca()>=CA_ENCRYPTED_MIN && device->ProvidesTransponder(channel)) {
+ cChannelCaids *ch=new cChannelCaids(channel);
+ if(ch) list.Add(ch);
+ }
+ }
+ Channels.Unlock();
+ list.Unique();
+ list.CheckIgnore();
+ list.Unique();
+
+ int n=0, h;
+ caid_t c[MAX_CI_SLOT_CAIDS+1];
+ memset(c,0,sizeof(c));
+ do {
+ if((h=list.Histo())<0) break;
+ c[n++]=h;
+ LBSTART(L_CORE_CAIDS);
+ LBPUT("%d: added %04x caids now",cardIndex,h); for(int i=0; i<n; i++) LBPUT(" %04x",c[i]);
+ LBEND();
+ list.Purge(h);
+ } while(n<MAX_CI_SLOT_CAIDS && list.Count()>0);
+ c[n]=0;
+ if(n==0) PRINTF(L_CORE_CI,"no active CAIDs");
+ else if(list.Count()>0) PRINTF(L_GEN_ERROR,"too many CAIDs. You should ignore some CAIDs.");
+
+ ciMutex.Lock();
+ if((version[0]==0 && c[0]!=0) || memcmp(caids[0],c,sizeof(caids[0]))) {
+ memcpy(caids[0],c,sizeof(caids[0]));
+ version[0]++;
+ if(version[0]>0) {
+ LBSTART(L_CORE_CI);
+ LBPUT("card %d, slot %d (v=%2d) caids:",cardIndex,0,version[0]);
+ for(int i=0; caids[0][i]; i++) LBPUT(" %04x",caids[0][i]);
+ LBEND();
+ }
+ }
+ ciMutex.Unlock();
+
+ caidTimer.Set(CAID_TIME);
+ }
+}
+
+int cScCiAdapter::Read(unsigned char *Buffer, int MaxLength)
+{
+ cMutexLock lock(&ciMutex);
+ if(cam && rb && Buffer && MaxLength>0) {
+ int c;
+ unsigned char *data=rb->Get(c);
+ if(data) {
+ int s=data[0];
+ if(c>=s+1) {
+ c=s>MaxLength ? MaxLength : s;
+ memcpy(Buffer,&data[1],c);
+ if(c<s) { data[c]=s-c; rb->Del(c); }
+ else rb->Del(c+1);
+ if(Buffer[2]!=0x80 || LOG(L_CORE_CIFULL)) {
+ LDUMP(L_CORE_CI,Buffer,c,"%d.%d <-",cardIndex,Buffer[0]);
+ readTimer.Set();
+ }
+ return c;
+ }
+ else {
+ LDUMP(L_GEN_DEBUG,data,c,"internal: sc-ci %d rb frame sync got=%d avail=%d -",cardIndex,c,rb->Available());
+ rb->Clear();
+ }
+ }
+ }
+ else cCondWait::SleepMs(CAM_READ_TIMEOUT);
+ if(LOG(L_CORE_CIFULL) && readTimer.Elapsed()>2000) {
+ PRINTF(L_CORE_CIFULL,"%d: read heartbeat",cardIndex);
+ readTimer.Set();
+ }
+ return 0;
+}
+
+#define TPDU(data,slot) do { unsigned char *_d=(data); _d[0]=(slot); _d[1]=tcid; } while(0)
+#define TAG(data,tag,len) do { unsigned char *_d=(data); _d[0]=(tag); _d[1]=(len); } while(0)
+#define SB_TAG(data,sb) do { unsigned char *_d=(data); _d[0]=0x80; _d[1]=0x02; _d[2]=tcid; _d[3]=(sb); } while(0)
+#define PUT_TAG(data,len) do { unsigned char *_d=(data)-1; int _l=(len); _d[0]=_l; if(rb) rb->Put(_d,_l+1); } while(0)
+
+void cScCiAdapter::Write(const unsigned char *buff, int len)
+{
+ cMutexLock lock(&ciMutex);
+ if(cam && buff && len>=5) {
+ unsigned char a[128], *b=&a[1];
+ struct TPDU *tpdu=(struct TPDU *)buff;
+ int slot=tpdu->slot;
+ if(buff[2]!=0xA0 || buff[3]>0x01 || LOG(L_CORE_CIFULL))
+ LDUMP(L_CORE_CI,buff,len,"%d.%d ->",cardIndex,slot);
+ if(slots[slot]) {
+ switch(tpdu->tag) {
+ case 0x81: // T_RCV
+ {
+ TPDU(b,slot);
+ int l=2, c;
+ unsigned char *d=slots[slot]->Get(c);
+ if(d) {
+ int s=d[0];
+ if(c>=s) {
+ memcpy(&b[l],&d[1],s);
+ l+=s;
+ slots[slot]->Del(s+1);
+ }
+ else slots[slot]->Del(c);
+ }
+ SB_TAG(&b[l],0x00);
+ PUT_TAG(b,l+4);
+ break;
+ }
+ case 0x82: // T_CREATE_TC
+ tcid=tpdu->data[0];
+ TPDU(b,slot);
+ TAG(&b[2],0x83,0x01); b[4]=tcid;
+ SB_TAG(&b[5],0x00);
+ PUT_TAG(b,9);
+
+ static const unsigned char reqCAS[] = { 0xA0,0x07,0x01,0x91,0x04,0x00,0x03,0x00,0x41 };
+ memcpy(b,reqCAS,sizeof(reqCAS));
+ b[2]=tcid;
+ a[0]=sizeof(reqCAS);
+ slots[slot]->Put(a,sizeof(reqCAS)+1);
+ break;
+ case 0xA0: // T_DATA_LAST
+ slots[slot]->Process(buff,len);
+ TPDU(b,slot);
+ SB_TAG(&b[2],slots[slot]->Available()>0 ? 0x80:0x00);
+ PUT_TAG(b,6);
+ break;
+ }
+ }
+ }
+ else PRINTF(L_CORE_CIFULL,"%d: short write (cam=%d buff=%d len=%d)",cardIndex,cam!=0,buff!=0,len);
+}
+
+bool cScCiAdapter::Reset(int Slot)
+{
+ cMutexLock lock(&ciMutex);
+ PRINTF(L_CORE_CI,"%d: reset of slot %d requested",cardIndex,Slot);
+ return slots[Slot] ? slots[Slot]->Reset():false;
+}
+
+eModuleStatus cScCiAdapter::ModuleStatus(int Slot)
+{
+ cMutexLock lock(&ciMutex);
+ bool enable=ScSetup.CapCheck(cardIndex);
+ if(!enable) CamStop();
+ return (enable && cam && slots[Slot]) ? slots[Slot]->Status():msNone;
+}
+
+bool cScCiAdapter::Assign(cDevice *Device, bool Query)
+{
+ return Device ? (Device==device) : true;
+}
+
+#endif //APIVERSNUM >= 10500
+
+// -- cDeCSA -------------------------------------------------------------------
+
+#define MAX_CSA_PIDS 8192
+#define MAX_CSA_IDX 16
+
+class cDeCSA : public cMutex {
+private:
+ int cs;
+ unsigned char **range;
+ unsigned char pidmap[MAX_CSA_PIDS];
+ void *keys[MAX_CSA_IDX];
+ //
+ bool GetKeyStruct(int idx);
+public:
+ cDeCSA(void);
+ ~cDeCSA();
+ bool Decrypt(unsigned char *data, int len, bool force);
+ bool SetDescr(ca_descr_t *ca_descr);
+ bool SetCaPid(ca_pid_t *ca_pid);
+ };
+
+cDeCSA::cDeCSA(void)
+{
+ cs=get_suggested_cluster_size();
+ PRINTF(L_CORE_CSA,"clustersize=%d rangesize=%d",cs,cs*2+5);
+ range=MALLOC(unsigned char *,(cs*2+5));
+ memset(keys,0,sizeof(keys));
+ memset(pidmap,0,sizeof(pidmap));
+}
+
+cDeCSA::~cDeCSA()
+{
+ for(int i=0; i<MAX_CSA_IDX; i++)
+ if(keys[i]) free_key_struct(keys[i]);
+ free(range);
+}
+
+bool cDeCSA::GetKeyStruct(int idx)
+{
+ if(!keys[idx]) keys[idx]=get_key_struct();
+ return keys[idx]!=0;
+}
+
+bool cDeCSA::SetDescr(ca_descr_t *ca_descr)
+{
+ cMutexLock lock(this);
+ int idx=ca_descr->index;
+ if(idx<MAX_CSA_IDX && GetKeyStruct(idx)) {
+ if(ca_descr->parity==0) { // even key
+ set_even_control_word(keys[idx],ca_descr->cw);
+ PRINTF(L_CORE_CSA,"even key set");
+ }
+ else { // odd key
+ set_odd_control_word(keys[idx],ca_descr->cw);
+ PRINTF(L_CORE_CSA,"odd key set");
+ }
+ }
+ return true;
+}
+
+bool cDeCSA::SetCaPid(ca_pid_t *ca_pid)
+{
+ cMutexLock lock(this);
+ if(ca_pid->index<MAX_CSA_IDX && ca_pid->pid<MAX_CSA_PIDS) {
+ pidmap[ca_pid->pid]=ca_pid->index;
+ PRINTF(L_CORE_CSA,"set pid %04x to index %d",ca_pid->pid,ca_pid->index);
+ }
+ return true;
+}
+
+bool cDeCSA::Decrypt(unsigned char *data, int len, bool force)
+{
+ cMutexLock lock(this);
+ int r=-2, ccs=0, currIdx=-1;
+ bool newRange=true;
+ range[0]=0;
+ len-=(TS_SIZE-1);
+ for(int l=0; l<len; l+=TS_SIZE) {
+ if(data[l]!=TS_SYNC_BYTE) { // let higher level cope with that
+ PRINTF(L_CORE_CSA,"garbage in TS buffer");
+ break;
+ }
+ if(data[l+3]&0xC0) { // encrypted
+ int idx=pidmap[((data[l+1]<<8)+data[l+2])&(MAX_CSA_PIDS-1)];
+ if(currIdx<0 || idx==currIdx) { // same or no index
+ currIdx=idx;
+ if(newRange) {
+ r+=2; newRange=false;
+ range[r]=&data[l];
+ range[r+2]=0;
+ }
+ range[r+1]=&data[l+TS_SIZE];
+ if(++ccs>=cs) break;
+ }
+ else { // other index, create hole
+ PRINTF(L_CORE_CSA,"other index. current=%d idx=%d",currIdx,idx);
+ newRange=true;
+ }
+ }
+ else { // unencrypted
+ // nothing, we don't create holes for unencrypted packets
+ }
+ }
+ if(force) PRINTF(L_CORE_CSA,"forced");
+ if(r>cs*2) PRINTF(L_CORE_CSA,"range overflow");
+ if(r<0) PRINTF(L_CORE_CSA,"nothing to decrypt");
+ if(r>=0 && (ccs>=cs || force) && GetKeyStruct(currIdx)) { // we have some range
+ LBSTARTF(L_CORE_CSA);
+ LBPUT("decrypting %3d packets (ccs=%3d cs=%3d %s)",ccs,ccs,cs,ccs>=cs?"OK ":"INC");
+ int n=decrypt_packets(keys[currIdx],range);
+ LBPUT(" -> %3d packets decrypted\n",n);
+ if(n>0) return true;
+ LBEND();
+ }
+ else {
+ if(ccs<cs && !force)
+ PRINTF(L_CORE_CSA,"incomplete cluster ccs=%3d cs=%3d",ccs,cs);
+ }
+ return false;
+}
+
+// -- cDeCsaTSBuffer -----------------------------------------------------------
+
+class cDeCsaTSBuffer : public cThread {
+private:
+ int f;
+ int cardIndex;
+ bool delivered;
+ cRingBufferLinear *ringBuffer;
+ //
+ cDeCSA *decsa;
+ bool scActive;
+ unsigned char *lastP;
+ int lastCount;
+ //
+ virtual void Action(void);
+public:
+ cDeCsaTSBuffer(int File, int Size, int CardIndex, cDeCSA *DeCsa, bool ScActive);
+ ~cDeCsaTSBuffer();
+ uchar *Get(void);
+ };
+
+cDeCsaTSBuffer::cDeCsaTSBuffer(int File, int Size, int CardIndex, cDeCSA *DeCsa, bool ScActive)
+{
+ SetDescription("TS buffer on device %d", CardIndex);
+ f=File; cardIndex=CardIndex; decsa=DeCsa; scActive=ScActive;
+ delivered=false;
+ lastP=0; lastCount=0;
+ ringBuffer=new cRingBufferLinear(Size,TS_SIZE,true,"FFdecsa-TS");
+ ringBuffer->SetTimeouts(100,100);
+ Start();
+}
+
+cDeCsaTSBuffer::~cDeCsaTSBuffer()
+{
+ Cancel(3);
+ delete ringBuffer;
+}
+
+void cDeCsaTSBuffer::Action(void)
+{
+ if(ringBuffer) {
+ bool firstRead=true;
+ cPoller Poller(f);
+ while(Running()) {
+ if(firstRead || Poller.Poll(100)) {
+ firstRead=false;
+ int r=ringBuffer->Read(f);
+ if(r<0 && FATALERRNO) {
+ if(errno==EOVERFLOW)
+ esyslog("ERROR: driver buffer overflow on device %d",cardIndex);
+ else { LOG_ERROR; break; }
+ }
+ }
+ }
+ }
+}
+
+uchar *cDeCsaTSBuffer::Get(void)
+{
+ int Count=0;
+ if(delivered) { ringBuffer->Del(TS_SIZE); delivered=false; }
+ uchar *p=ringBuffer->Get(Count);
+ if(p && Count>=TS_SIZE) {
+ if(*p!=TS_SYNC_BYTE) {
+ for(int i=1; i<Count; i++)
+ if(p[i]==TS_SYNC_BYTE) { Count=i; break; }
+ ringBuffer->Del(Count);
+ esyslog("ERROR: skipped %d bytes to sync on TS packet on device %d",Count,cardIndex);
+ return NULL;
+ }
+
+ if(scActive && (p[3]&0xC0)) {
+ if(decsa) {
+ if(!decsa->Decrypt(p,Count,(lastP==p && lastCount==Count))) {
+ lastP=p; lastCount=Count;
+ cCondWait::SleepMs(20);
+ return NULL;
+ }
+ lastP=0;
+ }
+ else p[3]&=~0xC0; // FF hack
+ }
+
+ delivered=true;
+ return p;
+ }
+ return NULL;
+}
+
+// -- cScDvbDevice -------------------------------------------------------------
+
+int cScDvbDevice::budget=0;
+
+cScDvbDevice::cScDvbDevice(int n, int cafd)
+:cDvbDevice(n)
+{
+ decsa=0; tsBuffer=0; cam=0;
+#if APIVERSNUM >= 10500
+ ciadapter=0; hwciadapter=0;
+#endif
+ memset(lrucaid,0,sizeof(lrucaid));
+ fd_ca=cafd; fd_ca2=dup(fd_ca); fd_dvr=-1;
+ softcsa=(fd_ca<0);
+}
+
+cScDvbDevice::~cScDvbDevice()
+{
+ DetachAllReceivers();
+ Cancel(3);
+ EarlyShutdown();
+ delete decsa;
+ if(fd_ca>=0) close(fd_ca);
+#if APIVERSNUM >= 10500
+ if(fd_ca2>=0) close(fd_ca2);
+#endif
+}
+
+void cScDvbDevice::EarlyShutdown(void)
+{
+#if APIVERSNUM >= 10500
+ SetCamSlot(0);
+ delete ciadapter; ciadapter=0;
+ delete hwciadapter; hwciadapter=0;
+#endif
+ if(cam) cam->Stop();
+ delete cam; cam=0;
+}
+
+void cScDvbDevice::LateInit(void)
+{
+ int n=CardIndex();
+ if(DeviceNumber()!=n)
+ PRINTF(L_GEN_ERROR,"CardIndex - DeviceNumber mismatch! Put SC plugin first on VDR commandline!");
+ if(softcsa) {
+ if(HasDecoder()) PRINTF(L_GEN_ERROR,"Card %d is a full-featured card but no ca device found!",n);
+ }
+ else if(ForceBudget(n)) {
+ PRINTF(L_GEN_INFO,"Budget mode forced on card %d",n);
+ softcsa=true;
+ }
+
+#if APIVERSNUM >= 10500
+ if(fd_ca2>=0) hwciadapter=cDvbCiAdapter::CreateCiAdapter(this,fd_ca2);
+ cam=new cCam(this,n);
+ ciadapter=new cScCiAdapter(this,n,cam);
+#else
+ if(fd_ca2>=0) {
+ ciHandler=cCiHandler::CreateCiHandler(fd_ca2);
+ if(!ciHandler) close(fd_ca2);
+ }
+ cam=ScSetup.CapCheck(n) ? new cCam(this,n):0;
+#endif
+ if(softcsa) {
+ PRINTF(L_GEN_INFO,"Using software decryption on card %d",n);
+ decsa=new cDeCSA;
+ }
+}
+
+void cScDvbDevice::Shutdown(void)
+{
+ for(int n=cDevice::NumDevices(); --n>=0;) {
+ cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(n));
+ if(dev) dev->EarlyShutdown();
+ }
+}
+
+void cScDvbDevice::Startup(void)
+{
+ for(int n=cDevice::NumDevices(); --n>=0;) {
+ cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(n));
+ if(dev) dev->LateInit();
+ }
+}
+
+void cScDvbDevice::SetForceBudget(int n)
+{
+ if(n>=0 && n<MAXDVBDEVICES) budget|=(1<<n);
+}
+
+bool cScDvbDevice::ForceBudget(int n)
+{
+ return budget && (budget&(1<<n));
+}
+
+static int *vdr_nci=0, *vdr_ud=0, vdr_save_ud;
+
+void cScDvbDevice::Capture(void)
+{
+/*
+ This is an extremly ugly hack to access VDRs device scan parameters, which are
+ protected in this context. Heavily dependant on the actual symbol names
+ created by the compiler. May fail in any future version!
+
+ To get the actual symbol names of your VDR binary you may use the command:
+ objdump -T <path-to-vdr>/vdr | grep -E "(useDevice|nextCardIndex)"
+ Insert the symbol names below.
+*/
+ vdr_nci=(int *)dlsym(RTLD_DEFAULT,"_ZN7cDevice13nextCardIndexE");
+ vdr_ud =(int *)dlsym(RTLD_DEFAULT,"_ZN7cDevice9useDeviceE");
+ if(vdr_nci && vdr_ud) { vdr_save_ud=*vdr_ud; *vdr_ud=1<<30; }
+}
+
+bool cScDvbDevice::Initialize(void)
+{
+ if(!vdr_nci || !vdr_ud) {
+ PRINTF(L_GEN_ERROR,"Failed to locate VDR symbols. Plugin not operable");
+ return false;
+ }
+ if(NumDevices()>0) {
+ PRINTF(L_GEN_ERROR,"Number of devices != 0 on init. Put SC plugin first on VDR commandline! Aborting.");
+ return false;
+ }
+ *vdr_nci=0; *vdr_ud=vdr_save_ud;
+
+ int i, found=0;
+ for(i=0; i<MAXDVBDEVICES; i++) {
+ if(UseDevice(NextCardIndex())) {
+ char name[128];
+ DvbName(DEV_DVB_FRONTEND,i,name,sizeof(name));
+ if(access(name,F_OK)==0) {
+ PRINTF(L_GEN_DEBUG,"probing %s",name);
+ int f=open(name,O_RDONLY);
+ if(f>=0) {
+ close(f);
+ PRINTF(L_GEN_DEBUG,"capturing device %d",i);
+ new cScDvbDevice(i,DvbOpen(DEV_DVB_CA,i,O_RDWR));
+ found++;
+ }
+ else {
+ if(errno!=ENODEV && errno!=EINVAL) PRINTF(L_GEN_ERROR,"open %s failed: %s",name,strerror(errno));
+ break;
+ }
+ }
+ else {
+ if(errno!=ENOENT) PRINTF(L_GEN_ERROR,"access %s failed: %s",name,strerror(errno));
+ break;
+ }
+ }
+ else NextCardIndex(1);
+ }
+ NextCardIndex(MAXDVBDEVICES-i);
+ if(found>0) PRINTF(L_GEN_INFO,"captured %d video device%s",found,found>1 ? "s" : "");
+ else PRINTF(L_GEN_INFO,"no DVB device captured");
+ return found>0;
+}
+
+#if APIVERSNUM >= 10501
+bool cScDvbDevice::HasCi(void)
+{
+ return ciadapter || hwciadapter;
+}
+#endif
+
+#if APIVERSNUM >= 10500
+bool cScDvbDevice::Ready(void)
+{
+ return (ciadapter ? ciadapter->Ready():true) &&
+ (hwciadapter ? hwciadapter->Ready():true);
+}
+#endif
+
+bool cScDvbDevice::SetPid(cPidHandle *Handle, int Type, bool On)
+{
+ if(cam) cam->SetPid(Type,Handle->pid,On);
+ return cDvbDevice::SetPid(Handle,Type,On);
+}
+
+bool cScDvbDevice::SetChannelDevice(const cChannel *Channel, bool LiveView)
+{
+ lruMutex.Lock();
+ int i=FindLRUPrg(Channel->Source(),Channel->Transponder(),Channel->Sid());
+ if(i<0) i=MAX_LRU_CAID-1;
+ if(i>0) memmove(&lrucaid[1],&lrucaid[0],sizeof(struct LruCaid)*i);
+#if APIVERSNUM >= 10500
+ const caid_t *c=Channel->Caids();
+ for(i=0; i<MAXCAIDS && *c; i++) lrucaid[0].caids[i]=*c++;
+ lrucaid[0].caids[i]=0;
+#else
+ for(i=0; i<=MAXCAIDS; i++) if((lrucaid[0].caids[i]=Channel->Ca(i))==0) break;
+#endif
+ lrucaid[0].src=Channel->Source();
+ lrucaid[0].tr=Channel->Transponder();
+ lrucaid[0].prg=Channel->Sid();
+ lruMutex.Unlock();
+ if(cam) cam->Tune(Channel);
+ return cDvbDevice::SetChannelDevice(Channel,LiveView);
+}
+
+bool cScDvbDevice::GetPrgCaids(int source, int transponder, int prg, caid_t *c)
+{
+ cMutexLock lock(&lruMutex);
+ int i=FindLRUPrg(source,transponder,prg);
+ if(i>=0) {
+ for(int j=0; j<MAXCAIDS && lrucaid[i].caids[j]; j++) *c++=lrucaid[i].caids[j];
+ *c=0;
+ return true;
+ }
+ return false;
+}
+
+int cScDvbDevice::FindLRUPrg(int source, int transponder, int prg)
+{
+ for(int i=0; i<MAX_LRU_CAID; i++)
+ if(lrucaid[i].src==source && lrucaid[i].tr==transponder && lrucaid[i].prg==prg) return i;
+ return -1;
+}
+
+#if APIVERSNUM < 10500
+int cScDvbDevice::ProvidesCa(const cChannel *Channel) const
+{
+ if(cam && Channel->Ca()>=CA_ENCRYPTED_MIN) {
+ int j;
+ caid_t ids[MAXCAIDS+1];
+ for(j=0; j<=MAXCAIDS; j++) if((ids[j]=Channel->Ca(j))==0) break;
+ if(cSystems::Provides(ids,!softcsa)>0) return 2;
+ }
+ return cDvbDevice::ProvidesCa(Channel);
+}
+
+bool cScDvbDevice::CiAllowConcurrent(void) const
+{
+ return softcsa || ScSetup.ConcurrentFF>0;
+}
+
+void cScDvbDevice::CiStartDecrypting(void)
+{
+ if(cam) {
+ cSimpleList<cPrg> prgList;
+ for(cCiCaProgramData *p=ciProgramList.First(); p; p=ciProgramList.Next(p)) {
+ if(p->modified) {
+ cPrg *prg=new cPrg(p->programNumber,cam->HasPrg(p->programNumber));
+ if(prg) {
+ for(cCiCaPidData *q=p->pidList.First(); q; q=p->pidList.Next(q)) {
+ if(q->active)
+ prg->pids.Add(new cPrgPid(q->streamType,q->pid));
+ }
+ prgList.Add(prg);
+ }
+ p->modified=false;
+ }
+ }
+ for(int loop=1; loop<=2; loop++) // first delete, then add
+ for(cPrg *prg=prgList.First(); prg; prg=prgList.Next(prg))
+ if((loop==1)!=(prg->pids.Count()>0))
+ cam->AddPrg(prg);
+ }
+ cDvbDevice::CiStartDecrypting();
+}
+#endif //APIVERSNUM < 10500
+
+bool cScDvbDevice::OpenDvr(void)
+{
+ CloseDvr();
+ fd_dvr=DvbOpen(DEV_DVB_DVR,CardIndex(),O_RDONLY|O_NONBLOCK,true);
+ if(fd_dvr>=0) {
+#if APIVERSNUM >= 10500
+ bool active=dynamic_cast<cScCamSlot *>(CamSlot())!=0;
+#else
+ bool active=cam && softcsa;
+#endif
+ tsBuffer=new cDeCsaTSBuffer(fd_dvr,MEGABYTE(2),CardIndex()+1,decsa,active);
+ }
+ return fd_dvr>=0;
+}
+
+void cScDvbDevice::CloseDvr(void)
+{
+ delete tsBuffer; tsBuffer=0;
+ if(fd_dvr>=0) { close(fd_dvr); fd_dvr=-1; }
+}
+
+bool cScDvbDevice::GetTSPacket(uchar *&Data)
+{
+ if(tsBuffer) { Data=tsBuffer->Get(); return true; }
+ return false;
+}
+
+bool cScDvbDevice::SetCaDescr(ca_descr_t *ca_descr)
+{
+ if(!softcsa) {
+ cMutexLock lock(&cafdMutex);
+ return ioctl(fd_ca,CA_SET_DESCR,ca_descr)>=0;
+ }
+ else if(decsa) return decsa->SetDescr(ca_descr);
+ return false;
+}
+
+bool cScDvbDevice::SetCaPid(ca_pid_t *ca_pid)
+{
+ if(!softcsa) {
+ cMutexLock lock(&cafdMutex);
+ return ioctl(fd_ca,CA_SET_PID,ca_pid)>=0;
+ }
+ else if(decsa) return decsa->SetCaPid(ca_pid);
+ return false;
+}
+
+static unsigned int av7110_read(int fd, unsigned int addr)
+{
+ ca_pid_t arg;
+ arg.pid=addr;
+ ioctl(fd,CA_GET_MSG,&arg);
+ return arg.index;
+}
+
+#if 0
+static void av7110_write(int fd, unsigned int addr, unsigned int val)
+{
+ ca_pid_t arg;
+ arg.pid=addr;
+ arg.index=val;
+ ioctl(fd,CA_SEND_MSG,&arg);
+}
+#endif
+
+void cScDvbDevice::DumpAV7110(void)
+{
+ if(LOG(L_CORE_AV7110)) {
+#define CODEBASE (0x2e000404+0x1ce00)
+ cMutexLock lock(&cafdMutex);
+ if(HasDecoder() && lastDump.Elapsed()>20000) {
+ lastDump.Set();
+ static unsigned int handles=0, hw_handles=0;
+ static const unsigned int code[] = {
+ 0xb5100040,0x4a095a12,0x48094282,0xd00b4b09,0x20000044,
+ 0x5b1c4294,0xd0033001,0x281cdbf8,0xe001f7fe,0xfd14bc10
+ };
+ static const unsigned int mask[] = {
+ 0xffffffff,0xffffffff,0xffffffff,0xffffffff,0xffffffff,
+ 0xffffffff,0xffffffff,0xffffffff,0xfffff800,0xf800ffff
+ };
+ if(!handles) {
+ handles=1;
+ PRINTF(L_CORE_AV7110,"searching handle tables");
+ for(int i=0; i<0x2000; i+=4) {
+ int j;
+ for(j=0; j<20; j+=4) {
+ int r=av7110_read(fd_ca,CODEBASE+i+j);
+ if((r&mask[j/4])!=(code[j/4]&mask[j/4])) break;
+ }
+ if(j==20) {
+ handles=av7110_read(fd_ca,CODEBASE+i+44);
+ hw_handles=av7110_read(fd_ca,CODEBASE+i+52);
+ PRINTF(L_CORE_AV7110,"found handles=%08x hw_handles=%08x at 0x%08x",handles,hw_handles,CODEBASE+i);
+ if((handles>>16)!=0x2e08 || (hw_handles>>16)!=0x2e08) {
+ PRINTF(L_CORE_AV7110,"seems to be invalid");
+ }
+ break;
+ }
+ }
+ }
+
+ unsigned int hdl=0, hwhdl=0;
+ PRINTF(L_CORE_AV7110," : 64000080 64000400");
+ for(int i=0; i<=31; i++) {
+ unsigned int off80 =av7110_read(fd_ca,0x64000080+i*4);
+ unsigned int off400=av7110_read(fd_ca,0x64000400+i*8);
+ LBSTART(L_CORE_AV7110);
+ LBPUT("handle %2d: %08x %08x %s pid=%04x idx=%d",
+ i,off80,off400,off80&0x2000?"ACT":"---",off80&0x1fff,(off400&0x1e)>>1);
+ if(handles>1 && i<=27) {
+ if((i&1)==0) {
+ hdl=av7110_read(fd_ca,handles+i*2);
+ hwhdl=av7110_read(fd_ca,hw_handles+i*2);
+ }
+ unsigned int s=((~i)&1)<<4;
+ LBPUT(" | %02d hdl=%04x hwfilt=%04x",i,(hdl>>s)&0xffff,(hwhdl>>s)&0xffff);
+ }
+ LBEND();
+ }
+ }
+ }
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___CAM_H
+#define ___CAM_H
+
+#include <linux/dvb/ca.h>
+#include <vdr/dvbdevice.h>
+#include <vdr/thread.h>
+#include "data.h"
+#include "misc.h"
+
+class cChannel;
+
+class cEcmHandler;
+class cEcmData;
+class cLogger;
+class cHookManager;
+class cLogHook;
+class cDeCSA;
+class cDeCsaTSBuffer;
+class cScCiAdapter;
+class cScDvbDevice;
+class cPrg;
+
+// ----------------------------------------------------------------
+
+class cEcmCache : public cLoader, cMutex, cSimpleList<cEcmData> {
+private:
+ cEcmData *Exists(cEcmInfo *e);
+public:
+ cEcmCache(void);
+ void Load(void);
+ void New(cEcmInfo *e);
+ int GetCached(cSimpleList<cEcmInfo> *list, int sid, int Source, int Transponder);
+ void Delete(cEcmInfo *e);
+ void Flush(void);
+ virtual bool Save(FILE *f);
+ virtual bool ParseLine(const char *line, bool fromCache);
+ };
+
+extern cEcmCache ecmcache;
+
+// ----------------------------------------------------------------
+
+#if APIVERSNUM >= 10500
+typedef int caid_t;
+#else
+typedef unsigned short caid_t;
+#endif
+
+#define MAX_CW_IDX 16
+#define MAX_CI_SLOTS 8
+#ifdef VDR_MAXCAID
+#define MAX_CI_SLOT_CAIDS VDR_MAXCAID
+#else
+#define MAX_CI_SLOT_CAIDS 16
+#endif
+
+class cCam : private cMutex {
+private:
+ int cardNum;
+ cScDvbDevice *device;
+ cSimpleList<cEcmHandler> handlerList;
+ cLogger *logger;
+ cHookManager *hookman;
+ int source, transponder, liveVpid, liveApid;
+ unsigned char indexMap[MAX_CW_IDX], lastCW[MAX_CW_IDX][2*8];
+ //
+ cEcmHandler *GetHandler(int sid, bool needZero, bool noshift);
+ void RemHandler(cEcmHandler *handler);
+ int GetFreeIndex(void);
+public:
+ cCam(cScDvbDevice *dev, int CardNum);
+ virtual ~cCam();
+ // EcmHandler API
+ void WriteCW(int index, unsigned char *cw, bool force);
+ void SetCWIndex(int pid, int index);
+ void DumpAV7110(void);
+ void LogEcmStatus(const cEcmInfo *ecm, bool on);
+ bool GetPrgCaids(int source, int transponder, int prg, caid_t *c);
+ void AddHook(cLogHook *hook);
+ bool TriggerHook(int id);
+ // Plugin API
+ bool Active(void);
+ void HouseKeeping(void);
+ void Tune(const cChannel *channel);
+ void SetPid(int type, int pid, bool on);
+ void Stop(void);
+ void AddPrg(cPrg *prg);
+ bool HasPrg(int prg);
+ char *CurrentKeyStr(int num);
+ //
+ bool IsSoftCSA(void);
+ int Source(void) { return source; }
+ int Transponder(void) { return transponder; }
+ };
+
+void LogStatsDown(void);
+
+// ----------------------------------------------------------------
+
+#define MAX_LRU_CAID 10
+
+class cScDvbDevice : public cDvbDevice {
+private:
+ cDeCSA *decsa;
+ cDeCsaTSBuffer *tsBuffer;
+#if APIVERSNUM >= 10500
+ cScCiAdapter *ciadapter;
+ cCiAdapter *hwciadapter;
+#endif
+ cCam *cam;
+ int fd_dvr, fd_ca, fd_ca2;
+ bool softcsa;
+ cMutex cafdMutex;
+ cTimeMs lastDump;
+ struct LruCaid {
+ int src, tr, prg;
+ caid_t caids[MAXCAIDS+1];
+ } lrucaid[MAX_LRU_CAID];
+ cMutex lruMutex;
+ static int budget;
+ //
+ void LateInit(void);
+ void EarlyShutdown(void);
+ int FindLRUPrg(int source, int transponder, int prg);
+protected:
+#if APIVERSNUM >= 10500
+ virtual bool Ready(void);
+#else
+ virtual void CiStartDecrypting(void);
+ virtual bool CiAllowConcurrent(void) const;
+#endif
+ virtual bool SetPid(cPidHandle *Handle, int Type, bool On);
+ virtual bool SetChannelDevice(const cChannel *Channel, bool LiveView);
+ virtual bool OpenDvr(void);
+ virtual void CloseDvr(void);
+ virtual bool GetTSPacket(uchar *&Data);
+public:
+ cScDvbDevice(int n, int cafd);
+ ~cScDvbDevice();
+#if APIVERSNUM >= 10501
+ virtual bool HasCi(void);
+#else
+ virtual int ProvidesCa(const cChannel *Channel) const;
+#endif
+ static void Capture(void);
+ static bool Initialize(void);
+ static void Startup(void);
+ static void Shutdown(void);
+ static void SetForceBudget(int n);
+ static bool ForceBudget(int n);
+ bool SetCaDescr(ca_descr_t *ca_descr);
+ bool SetCaPid(ca_pid_t *ca_pid);
+ void DumpAV7110(void);
+ cCam *Cam(void) { return cam; }
+ bool SoftCSA(void) { return softcsa; }
+ bool GetPrgCaids(int source, int transponder, int prg, caid_t *c);
+ };
+
+#endif // ___CAM_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___COMMON_H
+#define ___COMMON_H
+
+// Debugging
+#define DEBUG
+//#define DEBUG_DYN
+#define DEBUG_LOG_FILE "/var/tmp/logger.log"
+
+#ifdef DEBUG
+#define d(x) { (x); }
+#else
+#define d(x) ;
+#endif
+
+#ifdef DEBUG_DYN
+#define dyn(x) { (x); }
+#else
+#define dyn(x) ;
+#endif
+
+#endif //___COMMON_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___CRYPTO_BN_H
+#define ___CRYPTO_BN_H
+
+#include <openssl/bn.h>
+
+// ----------------------------------------------------------------
+
+void RotateBytes(unsigned char *in, int n);
+void RotateBytes(unsigned char *out, const unsigned char *in, int n);
+
+// ----------------------------------------------------------------
+
+class cBN {
+private:
+ BIGNUM big;
+public:
+ cBN(void) { BN_init(&big); }
+ ~cBN() { BN_free(&big); }
+ operator BIGNUM* () { return &big; }
+ bool Get(const unsigned char *in, int n);
+ bool GetLE(const unsigned char *in, int n);
+ int Put(unsigned char *out, int n) const;
+ int PutLE(unsigned char *out, int n) const;
+ };
+
+class cBNctx {
+private:
+ BN_CTX *ctx;
+public:
+ cBNctx(void) { ctx=BN_CTX_new(); }
+ ~cBNctx() { BN_CTX_free(ctx); }
+ operator BN_CTX* () { return ctx; }
+ };
+
+#endif //___CRYPTO_BN_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "crypto.h"
+#include "helper.h"
+#include "log.h"
+
+// -----------------------------------------------------------------------------
+
+void RotateBytes(unsigned char *out, const unsigned char *in, int n)
+{
+ // loop is executed atleast once, so it's not a good idea to
+ // call with n=0 !!
+ out+=n;
+ do { *(--out)=*(in++); } while(--n);
+}
+
+void RotateBytes(unsigned char *in, int n)
+{
+ // loop is executed atleast once, so it's not a good idea to
+ // call with n=0 !!
+ unsigned char *e=in+n-1;
+ do {
+ unsigned char temp=*in;
+ *in++=*e;
+ *e-- =temp;
+ } while(in<e);
+}
+
+// -- cBN ----------------------------------------------------------------------
+
+bool cBN::Get(const unsigned char *in, int n)
+{
+ return BN_bin2bn(in,n,&big)!=0;
+}
+
+int cBN::Put(unsigned char *out, int n) const
+{
+ int s=BN_num_bytes(&big);
+ if(s>n) {
+ unsigned char buff[s];
+ BN_bn2bin(&big,buff);
+ memcpy(out,buff+s-n,n);
+ }
+ else if(s<n) {
+ int l=n-s;
+ memset(out,0,l);
+ BN_bn2bin(&big,out+l);
+ }
+ else BN_bn2bin(&big,out);
+ return s;
+}
+
+bool cBN::GetLE(const unsigned char *in, int n)
+{
+ unsigned char tmp[n];
+ RotateBytes(tmp,in,n);
+ return BN_bin2bn(tmp,n,&big)!=0;
+}
+
+int cBN::PutLE(unsigned char *out, int n) const
+{
+ int s=Put(out,n);
+ RotateBytes(out,n);
+ return s;
+}
+
+// -- cIDEA --------------------------------------------------------------------
+
+#ifndef OPENSSL_HAS_IDEA
+#warning ** openssl lacks IDEA support. Using deprecated static support code. Update your openssl package.
+#include "support/i_cbc.c"
+#include "support/i_skey.c"
+#endif
+
+void cIDEA::Decrypt(unsigned char *data, int len, const unsigned char *key, unsigned char *iv) const
+{
+ unsigned char v[8];
+ if(!iv) { memset(v,0,sizeof(v)); iv=v; }
+ IDEA_KEY_SCHEDULE ks;
+ idea_set_encrypt_key(key,&ks);
+ idea_cbc_encrypt(data,data,len&~7,&ks,iv,IDEA_DECRYPT);
+}
+
+int cIDEA::Encrypt(const unsigned char *data, int len, unsigned char *crypt, const unsigned char *key, unsigned char *iv) const
+{
+ unsigned char v[8];
+ if(!iv) { memset(v,0,sizeof(v)); iv=v; }
+ len&=~7;
+ IDEA_KEY_SCHEDULE ks;
+ idea_set_encrypt_key(key,&ks);
+ idea_cbc_encrypt(data,crypt,len,&ks,iv,IDEA_ENCRYPT);
+ return len;
+}
+
+void cIDEA::SetEncKey(const unsigned char *key, IdeaKS *ks) const
+{
+ idea_set_encrypt_key(key,ks);
+}
+
+void cIDEA::SetDecKey(const unsigned char *key, IdeaKS *ks) const
+{
+ IDEA_KEY_SCHEDULE tmp;
+ idea_set_encrypt_key(key,&tmp);
+ idea_set_decrypt_key(&tmp,ks);
+}
+
+void cIDEA::Decrypt(unsigned char *data, int len, IdeaKS *ks, unsigned char *iv) const
+{
+ unsigned char v[8];
+ if(!iv) { memset(v,0,sizeof(v)); iv=v; }
+ idea_cbc_encrypt(data,data,len&~7,ks,iv,IDEA_DECRYPT);
+}
+
+int cIDEA::Encrypt(const unsigned char *data, int len, unsigned char *crypt, IdeaKS *ks, unsigned char *iv) const
+{
+ unsigned char v[8];
+ if(!iv) { memset(v,0,sizeof(v)); iv=v; }
+ idea_cbc_encrypt(data,crypt,len&~7,ks,iv,IDEA_ENCRYPT);
+ return len;
+}
+
+// -- cRSA ---------------------------------------------------------------------
+
+bool cRSA::Input(cBN *d, const unsigned char *in, int n, bool LE) const
+{
+ if(LE) return d->GetLE(in,n);
+ else return d->Get(in,n);
+}
+
+int cRSA::Output(unsigned char *out, int n, cBN *r, bool LE) const
+{
+ if(LE) return r->PutLE(out,n);
+ else return r->Put(out,n);
+}
+
+int cRSA::RSA(unsigned char *out, const unsigned char *in, int n, const BIGNUM *exp, const BIGNUM *mod, bool LE) const
+{
+ cBNctx ctx;
+ cBN r, d;
+ if(Input(&d,in,n,LE)) {
+ if(BN_mod_exp(r,d,exp,mod,ctx)) return Output(out,n,&r,LE);
+ PRINTF(L_GEN_ERROR,"rsa: mod-exp failed");
+ }
+ return 0;
+}
+
+int cRSA::RSA(BIGNUM *out, const unsigned char *in, int n, const BIGNUM *exp, const BIGNUM *mod, bool LE) const
+{
+ cBNctx ctx;
+ cBN d;
+ if(Input(&d,in,n,LE)) {
+ if(BN_mod_exp(out,d,exp,mod,ctx)) return BN_num_bytes(out);
+ PRINTF(L_GEN_ERROR,"rsa: mod-exp failed");
+ }
+ return 0;
+}
+
+int cRSA::RSA(unsigned char *out, int n, BIGNUM *in, const BIGNUM *exp, const BIGNUM *mod, bool LE) const
+{
+ cBNctx ctx;
+ cBN r;
+ if(BN_mod_exp(r,in,exp,mod,ctx)) return Output(out,n,&r,LE);
+ PRINTF(L_GEN_ERROR,"rsa: mod-exp failed");
+ return 0;
+}
+
+// -- cDes ---------------------------------------------------------------------
+
+const unsigned char cDes::_PC1[] = {
+ 57,49,41,33,25,17, 9, 1,
+ 58,50,42,34,26,18,10, 2,
+ 59,51,43,35,27,19,11, 3,
+ 60,52,44,36,63,55,47,39,
+ 31,23,15, 7,62,54,46,38,
+ 30,22,14, 6,61,53,45,37,
+ 29,21,13, 5,28,20,12, 4
+ };
+
+const unsigned char cDes::_PC2[] = {
+ 14,17,11,24, 1, 5, 3,28,15, 6,21,10,
+ 23,19,12, 4,26, 8, 16, 7,27,20,13, 2,
+ 41,52,31,37,47,55, 30,40,51,45,33,48,
+ 44,49,39,56,34,53, 46,42,50,36,29,32
+ };
+
+const unsigned char cDes::_E[] = {
+ 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9,
+ 8, 9,10,11,12,13, 12,13,14,15,16,17,
+ 16,17,18,19,20,21, 20,21,22,23,24,25,
+ 24,25,26,27,28,29, 28,29,30,31,32, 1
+ };
+
+const unsigned char cDes::_P[] = {
+ 16, 7,20,21, 29,12,28,17,
+ 1,15,23,26, 5,18,31,10,
+ 2, 8,24,14, 32,27, 3, 9,
+ 19,13,30, 6, 22,11, 4,25
+ };
+
+const unsigned char cDes::IP[] = {
+ 58,50,42,34,26,18,10,2, 60,52,44,36,28,20,12,4,
+ 62,54,46,38,30,22,14,6, 64,56,48,40,32,24,16,8,
+ 57,49,41,33,25,17, 9,1, 59,51,43,35,27,19,11,3,
+ 61,53,45,37,29,21,13,5, 63,55,47,39,31,23,15,7
+ };
+
+const unsigned char cDes::FP[] = {
+ 40,8,48,16,56,24,64,32, 39,7,47,15,55,23,63,31,
+ 38,6,46,14,54,22,62,30, 37,5,45,13,53,21,61,29,
+ 36,4,44,12,52,20,60,28, 35,3,43,11,51,19,59,27,
+ 34,2,42,10,50,18,58,26, 33,1,41, 9,49,17,57,25
+ };
+
+const unsigned char cDes::S[][64] = {
+ { 0xe,0x0,0x4,0xf,0xd,0x7,0x1,0x4, 0x2,0xe,0xf,0x2,0xb,0xd,0x8,0x1,
+ 0x3,0xa,0xa,0x6,0x6,0xc,0xc,0xb, 0x5,0x9,0x9,0x5,0x0,0x3,0x7,0x8,
+ 0x4,0xf,0x1,0xc,0xe,0x8,0x8,0x2, 0xd,0x4,0x6,0x9,0x2,0x1,0xb,0x7,
+ 0xf,0x5,0xc,0xb,0x9,0x3,0x7,0xe, 0x3,0xa,0xa,0x0,0x5,0x6,0x0,0xd
+ },
+ { 0xf,0x3,0x1,0xd,0x8,0x4,0xe,0x7, 0x6,0xf,0xb,0x2,0x3,0x8,0x4,0xe,
+ 0x9,0xc,0x7,0x0,0x2,0x1,0xd,0xa, 0xc,0x6,0x0,0x9,0x5,0xb,0xa,0x5,
+ 0x0,0xd,0xe,0x8,0x7,0xa,0xb,0x1, 0xa,0x3,0x4,0xf,0xd,0x4,0x1,0x2,
+ 0x5,0xb,0x8,0x6,0xc,0x7,0x6,0xc, 0x9,0x0,0x3,0x5,0x2,0xe,0xf,0x9
+ },
+ { 0xa,0xd,0x0,0x7,0x9,0x0,0xe,0x9, 0x6,0x3,0x3,0x4,0xf,0x6,0x5,0xa,
+ 0x1,0x2,0xd,0x8,0xc,0x5,0x7,0xe, 0xb,0xc,0x4,0xb,0x2,0xf,0x8,0x1,
+ 0xd,0x1,0x6,0xa,0x4,0xd,0x9,0x0, 0x8,0x6,0xf,0x9,0x3,0x8,0x0,0x7,
+ 0xb,0x4,0x1,0xf,0x2,0xe,0xc,0x3, 0x5,0xb,0xa,0x5,0xe,0x2,0x7,0xc
+ },
+ { 0x7,0xd,0xd,0x8,0xe,0xb,0x3,0x5, 0x0,0x6,0x6,0xf,0x9,0x0,0xa,0x3,
+ 0x1,0x4,0x2,0x7,0x8,0x2,0x5,0xc, 0xb,0x1,0xc,0xa,0x4,0xe,0xf,0x9,
+ 0xa,0x3,0x6,0xf,0x9,0x0,0x0,0x6, 0xc,0xa,0xb,0x1,0x7,0xd,0xd,0x8,
+ 0xf,0x9,0x1,0x4,0x3,0x5,0xe,0xb, 0x5,0xc,0x2,0x7,0x8,0x2,0x4,0xe
+ },
+ { 0x2,0xe,0xc,0xb,0x4,0x2,0x1,0xc, 0x7,0x4,0xa,0x7,0xb,0xd,0x6,0x1,
+ 0x8,0x5,0x5,0x0,0x3,0xf,0xf,0xa, 0xd,0x3,0x0,0x9,0xe,0x8,0x9,0x6,
+ 0x4,0xb,0x2,0x8,0x1,0xc,0xb,0x7, 0xa,0x1,0xd,0xe,0x7,0x2,0x8,0xd,
+ 0xf,0x6,0x9,0xf,0xc,0x0,0x5,0x9, 0x6,0xa,0x3,0x4,0x0,0x5,0xe,0x3
+ },
+ { 0xc,0xa,0x1,0xf,0xa,0x4,0xf,0x2, 0x9,0x7,0x2,0xc,0x6,0x9,0x8,0x5,
+ 0x0,0x6,0xd,0x1,0x3,0xd,0x4,0xe, 0xe,0x0,0x7,0xb,0x5,0x3,0xb,0x8,
+ 0x9,0x4,0xe,0x3,0xf,0x2,0x5,0xc, 0x2,0x9,0x8,0x5,0xc,0xf,0x3,0xa,
+ 0x7,0xb,0x0,0xe,0x4,0x1,0xa,0x7, 0x1,0x6,0xd,0x0,0xb,0x8,0x6,0xd
+ },
+ { 0x4,0xd,0xb,0x0,0x2,0xb,0xe,0x7, 0xf,0x4,0x0,0x9,0x8,0x1,0xd,0xa,
+ 0x3,0xe,0xc,0x3,0x9,0x5,0x7,0xc, 0x5,0x2,0xa,0xf,0x6,0x8,0x1,0x6,
+ 0x1,0x6,0x4,0xb,0xb,0xd,0xd,0x8, 0xc,0x1,0x3,0x4,0x7,0xa,0xe,0x7,
+ 0xa,0x9,0xf,0x5,0x6,0x0,0x8,0xf, 0x0,0xe,0x5,0x2,0x9,0x3,0x2,0xc
+ },
+ { 0xd,0x1,0x2,0xf,0x8,0xd,0x4,0x8, 0x6,0xa,0xf,0x3,0xb,0x7,0x1,0x4,
+ 0xa,0xc,0x9,0x5,0x3,0x6,0xe,0xb, 0x5,0x0,0x0,0xe,0xc,0x9,0x7,0x2,
+ 0x7,0x2,0xb,0x1,0x4,0xe,0x1,0x7, 0x9,0x4,0xc,0xa,0xe,0x8,0x2,0xd,
+ 0x0,0xf,0x6,0xc,0xa,0x9,0xd,0x0, 0xf,0x3,0x3,0x5,0x5,0x6,0x8,0xb
+ } };
+
+const unsigned char cDes::LS[] = { 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1 };
+
+/* actualy slower ... :-(
+#if defined __GNUC__ && __GNUC__ >= 2
+#define shiftin(V,R,n) ({unsigned int VV; \
+ __asm__ ("btl %3,%2\n\tadcl %0,%0" : "=g" (VV) : "0" (V), "g" (R), "r" (n) : "cc"); \
+ VV; })
+#else
+*/
+#define shiftin(V,R,n) ((V<<1)+(((R)>>(n))&1))
+#define rol28(V,n) ((V<<(n) ^ V>>(28-(n)))&0xfffffffL)
+#define ror28(V,n) ((V>>(n) ^ V<<(28-(n)))&0xfffffffL)
+
+#if defined __GNUC__ && __GNUC__ >= 2
+#if defined __i486__ || defined __pentium__ || defined __pentiumpro__ || defined __amd64__
+#define hash(T) ({unsigned int TT; \
+ __asm__("bswapl %k0\n\trorw $8,%w0\n\tbswapl %k0" : "=g" (TT) : "0" (T) : "cc"); \
+ TT; })
+#elif defined __i386__
+#define hash(T) ({unsigned int TT; \
+ __asm__("rorl $16,%k0\n\trorw $8,%w0\n\troll $16,%k0" : "=g" (TT) : "0" (T) : "cc"); \
+ TT; })
+#endif
+#endif
+#ifndef hash // fallback default
+#define hash(T) ((T&0x0000ffffL) | ((T>>8)&0x00ff0000L) | ((T<<8)&0xff000000L))
+#endif
+
+#define DESROUND(C,D,T) { \
+ unsigned int s=0; \
+ for(int j=7, k=0; j>=0; j--) { \
+ unsigned int v=0, K=0; \
+ for(int t=5; t>=0; t--, k++) { \
+ v=shiftin(v,T,E[k]); \
+ if(PC2[k]<29) K=shiftin(K,C,28-PC2[k]); \
+ else K=shiftin(K,D,56-PC2[k]); \
+ } \
+ s=(s<<4) + S[7-j][v^K]; \
+ } \
+ T=0; \
+ for(int j=31; j>=0; j--) T=shiftin(T,s,P[j]); \
+ }
+
+#define DESHASH(T) { if(mode&DES_HASH) T=hash(T); }
+
+cDes::cDes(const unsigned char *pc1, const unsigned char *pc2)
+{
+ PC1=pc1 ? pc1 : _PC1;
+ PC2=pc2 ? pc2 : _PC2;
+ for(int i=0; i<48; i++) E[i]=32-_E[i];
+ for(int i=0; i<32; i++) P[i]=32-_P[31-i];
+}
+
+void cDes::Permute(unsigned char *data, const unsigned char *P, int n) const
+{
+ unsigned char pin[8];
+ for(int i=0, k=0; k<n; i++) {
+ int p=0;
+ for(int j=7; j>=0; j--,k++) {
+ const int t=P[k]-1;
+ p=shiftin(p,data[t>>3],7-(t&7));
+ }
+ pin[i]=p;
+ }
+ memcpy(data,pin,8);
+}
+
+void cDes::Des(unsigned char *data, const unsigned char *key, int mode) const
+{
+ unsigned char mkey[8];
+ if(mode&DES_PC1) {
+ memcpy(mkey,key,sizeof(mkey));
+ Permute(mkey,PC1,56);
+ key=mkey;
+ }
+ if(mode&DES_IP) Permute(data,IP,64);
+ unsigned int C=UINT32_BE(key ) >> 4;
+ unsigned int D=UINT32_BE(key+3) & 0xfffffffL;
+ unsigned int L=UINT32_BE(data );
+ unsigned int R=UINT32_BE(data+4);
+ if(!(mode&DES_RIGHT)) {
+ for(int i=15; i>=0; i--) {
+ C=rol28(C,LS[15-i]); D=rol28(D,LS[15-i]);
+ unsigned int T=R;
+ if(mode&DES_MOD) T=Mod(T,key[7]); // apply costum mod e.g. Viaccess
+ DESROUND(C,D,T);
+ DESHASH(T);
+ T^=L; L=R; R=T;
+ }
+ }
+ else {
+ for(int i=15; i>=0; i--) {
+ unsigned int T=R;
+ if(mode&DES_MOD) T=Mod(T,key[7]); // apply costum mod e.g. Viaccess
+ DESROUND(C,D,T);
+ DESHASH(T);
+ T^=L; L=R; R=T;
+ C=ror28(C,LS[i]); D=ror28(D,LS[i]);
+ }
+ }
+ BYTE4_BE(data ,R);
+ BYTE4_BE(data+4,L);
+ if(mode&DES_FP) Permute(data,FP,64);
+}
+
+// -- cAES ---------------------------------------------------------------------
+
+#ifndef OPENSSL_HAS_AES
+#warning ** openssl lacks AES support. Using deprecated static support code. Update your openssl package.
+#include "support/aes_core.c"
+#endif
+
+cAES::cAES(void)
+{
+ active=false;
+}
+
+void cAES::SetKey(const unsigned char *key)
+{
+ AES_set_decrypt_key(key,128,&dkey);
+ AES_set_encrypt_key(key,128,&ekey);
+ active=true;
+}
+
+int cAES::Encrypt(const unsigned char *data, int len, unsigned char *crypt) const
+{
+ if(active) {
+ len=(len+15)&(~15); // pad up to a multiple of 16
+ for(int i=0; i<len; i+=16) AES_encrypt(data+i,crypt+i,(const AES_KEY *)&ekey);
+ return len;
+ }
+ return -1;
+}
+
+void cAES::Decrypt(unsigned char *data, int len) const
+{
+ if(active)
+ for(int i=0; i<len; i+=16) AES_decrypt(data+i,data+i,(const AES_KEY *)&dkey);
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___CRYPTO_H
+#define ___CRYPTO_H
+
+//
+// NOTE: all crypto classes SHOULD be fully reentrant.
+// They may be called from different threads concurrently.
+//
+// NOTE: cAES is not fully reentrant. Encrypt/Decrypt are reentrant
+// (as AES_KEY is const there), but SetKey is not. Be carefull.
+//
+
+#define OPENSSL_ALGORITHM_DEFINES
+#include <openssl/opensslconf.h>
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER < 0x0090700fL
+#error Openssl version 0.9.7 or newer is strongly recomended
+#endif
+
+#include "crypto-bn.h"
+
+// ----------------------------------------------------------------
+
+#define DES_LEFT 0
+#define DES_RIGHT 1
+
+#define DES_HASH 8
+#define DES_PC1 16
+#define DES_IP 32
+#define DES_FP 64
+#define DES_MOD 128
+
+// common DES modes
+
+#define PRV_DES_ENCRYPT (DES_LEFT|DES_PC1|DES_IP|DES_FP)
+#define PRV_DES_DECRYPT (DES_RIGHT|DES_PC1|DES_IP|DES_FP)
+
+#define VIA_DES (DES_LEFT|DES_MOD)
+#define VIA_DES_HASH (DES_LEFT|DES_HASH|DES_MOD)
+#define SECA_DES_ENCR PRV_DES_ENCRYPT
+#define SECA_DES_DECR PRV_DES_DECRYPT
+#define NAGRA_DES_ENCR PRV_DES_ENCRYPT
+#define NAGRA_DES_DECR PRV_DES_DECRYPT
+
+class cDes {
+private:
+ static const unsigned char _E[], _P[], _PC1[], _PC2[], IP[], FP[];
+protected:
+ static const unsigned char S[][64], LS[];
+ const unsigned char *PC1, *PC2;
+ unsigned char E[48], P[32];
+ //
+ virtual unsigned int Mod(unsigned int R, unsigned int key7) const { return R; }
+ void Permute(unsigned char *data, const unsigned char *P, int n) const;
+public:
+ cDes(const unsigned char *pc1=0, const unsigned char *pc2=0);
+ virtual ~cDes() {}
+ void Des(unsigned char *data, const unsigned char *key, int mode) const;
+ };
+
+// ----------------------------------------------------------------
+
+#if defined(OPENSSL_NO_AES) | defined(NO_AES) | OPENSSL_VERSION_NUMBER<0x0090700fL
+#include "support/aes.h"
+#else
+#define OPENSSL_HAS_AES
+#include <openssl/aes.h>
+#endif
+
+class cAES {
+private:
+ bool active;
+ AES_KEY dkey, ekey;
+protected:
+ void SetKey(const unsigned char *key);
+ void Decrypt(unsigned char *data, int len) const ;
+ int Encrypt(const unsigned char *data, int len, unsigned char *crypt) const;
+public:
+ cAES(void);
+ };
+
+// ----------------------------------------------------------------
+
+#if defined(OPENSSL_NO_IDEA) | defined(NO_IDEA) | OPENSSL_VERSION_NUMBER<0x0090700fL
+#include "support/idea.h"
+#else
+#define OPENSSL_HAS_IDEA
+#include <openssl/idea.h>
+#endif
+
+typedef IDEA_KEY_SCHEDULE IdeaKS;
+
+class cIDEA {
+public:
+ // single shot API
+ void Decrypt(unsigned char *data, int len, const unsigned char *key, unsigned char *iv) const;
+ int Encrypt(const unsigned char *data, int len, unsigned char *crypt, const unsigned char *key, unsigned char *iv) const;
+ // multi shot API
+ void SetEncKey(const unsigned char *key, IdeaKS *ks) const;
+ void SetDecKey(const unsigned char *key, IdeaKS *ks) const;
+ void Decrypt(unsigned char *data, int len, IdeaKS *ks, unsigned char *iv) const;
+ int Encrypt(const unsigned char *data, int len, unsigned char *crypt, IdeaKS *ks, unsigned char *iv) const;
+ };
+
+// ----------------------------------------------------------------
+
+class cRSA {
+private:
+ bool Input(cBN *d, const unsigned char *in, int n, bool LE) const;
+ int Output(unsigned char *out, int n, cBN *r, bool LE) const;
+public:
+ int RSA(unsigned char *out, const unsigned char *in, int n, const BIGNUM *exp, const BIGNUM *mod, bool LE=true) const;
+ int RSA(BIGNUM *out, const unsigned char *in, int n, const BIGNUM *exp, const BIGNUM *mod, bool LE=true) const;
+ int RSA(unsigned char *out, int len, BIGNUM *in, const BIGNUM *exp, const BIGNUM *mod, bool LE=true) const;
+ };
+
+#endif //___CRYPTO_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <vdr/tools.h>
+
+#include "data.h"
+#include "misc.h"
+#include "scsetup.h"
+#include "log-core.h"
+
+#define KEY_FILE "SoftCam.Key"
+#define CACACHE_FILE "ca.cache"
+#define EXT_AU_INT (15*60*1000) // ms interval for external AU
+#define EXT_AU_MIN ( 2*60*1000) // ms min. interval for external AU
+
+// -- cFileMap -----------------------------------------------------------------
+
+cFileMap::cFileMap(const char *Filename, bool Rw)
+{
+ filename=strdup(Filename);
+ rw=Rw;
+ fd=-1; count=len=0; addr=0; failed=false;
+}
+
+cFileMap::~cFileMap()
+{
+ Clean();
+ free(filename);
+}
+
+bool cFileMap::IsFileMap(const char *Name, bool Rw)
+{
+ return (!strcmp(Name,filename) && (!Rw || rw));
+}
+
+void cFileMap::Clean(void)
+{
+ if(addr) { munmap(addr,len); addr=0; len=0; }
+ if(fd>=0) { close(fd); fd=-1; }
+}
+
+bool cFileMap::Map(void)
+{
+ cMutexLock lock(this);
+ if(addr) { count++; return true; }
+ if(!failed) {
+ struct stat64 ds;
+ if(!stat64(filename,&ds)) {
+ if(S_ISREG(ds.st_mode)) {
+ fd=open(filename,rw ? O_RDWR : O_RDONLY);
+ if(fd>=0) {
+ unsigned char *map=(unsigned char *)mmap(0,ds.st_size,rw ? (PROT_READ|PROT_WRITE):(PROT_READ),MAP_SHARED,fd,0);
+ if(map!=MAP_FAILED) {
+ addr=map; len=ds.st_size; count=1;
+ return true;
+ }
+ else PRINTF(L_GEN_ERROR,"mapping failed on %s: %s",filename,strerror(errno));
+ close(fd); fd=-1;
+ }
+ else PRINTF(L_GEN_ERROR,"error opening filemap %s: %s",filename,strerror(errno));
+ }
+ else PRINTF(L_GEN_ERROR,"filemap %s is not a regular file",filename);
+ }
+ else PRINTF(L_GEN_ERROR,"can't stat filemap %s: %s",filename,strerror(errno));
+ failed=true; // don't try this one over and over again
+ }
+ return false;
+}
+
+bool cFileMap::Unmap(void)
+{
+ cMutexLock lock(this);
+ if(addr) {
+ if(!(--count)) { Clean(); return true; }
+ else Sync();
+ }
+ return false;
+}
+
+void cFileMap::Sync(void)
+{
+ cMutexLock lock(this);
+ if(addr) msync(addr,len,MS_ASYNC);
+}
+
+// -- cFileMaps ----------------------------------------------------------------
+
+cFileMaps filemaps;
+
+cFileMaps::cFileMaps(void)
+{
+ cfgDir=0;
+}
+
+cFileMaps::~cFileMaps()
+{
+ Clear();
+ free(cfgDir);
+}
+
+void cFileMaps::SetCfgDir(const char *CfgDir)
+{
+ free(cfgDir);
+ cfgDir=strdup(CfgDir);
+}
+
+cFileMap *cFileMaps::GetFileMap(const char *name, const char *domain, bool rw)
+{
+ cMutexLock lock(this);
+ char path[256];
+ snprintf(path,sizeof(path),"%s/%s/%s",cfgDir,domain,name);
+ cFileMap *fm=First();
+ while(fm) {
+ if(fm->IsFileMap(path,rw)) return fm;
+ fm=Next(fm);
+ }
+ fm=new cFileMap(path,rw);
+ Add(fm);
+ return fm;
+}
+
+// -- cConfRead ----------------------------------------------------------------
+
+bool cConfRead::ConfRead(const char *type, const char *filename)
+{
+ bool res=false;
+ FILE *f=fopen(filename,"r");
+ if(f) {
+ res=true;
+ PRINTF(L_GEN_INFO,"loading %s from %s",type,filename);
+ char buff[1024];
+ while(fgets(buff,sizeof(buff),f)) {
+ if(!index(buff,'\n') && !feof(f))
+ PRINTF(L_GEN_ERROR,"confread %s fgets readbuffer overflow",type);
+ char *line=skipspace(stripspace(buff));
+ if(line[0]==0 || line[0]==';' || line[0]=='#') continue; // skip empty & comment lines
+ if(!ParseLine(line,false)) {
+ PRINTF(L_GEN_ERROR,"file '%s' has error in line '%s'",filename,buff);
+ res=false;
+ }
+ }
+ fclose(f);
+ }
+ else PRINTF(L_GEN_ERROR,"Failed to open file '%s': %s",filename,strerror(errno));
+ return res;
+}
+
+// -- cLineDummy ---------------------------------------------------------------
+
+class cLineDummy : public cSimpleItem {
+private:
+ char *store;
+public:
+ cLineDummy(void);
+ ~cLineDummy();
+ bool Parse(const char *line);
+ bool Save(FILE *f);
+ };
+
+cLineDummy::cLineDummy(void)
+{
+ store=0;
+}
+
+cLineDummy::~cLineDummy()
+{
+ free(store);
+}
+
+bool cLineDummy::Parse(const char *line)
+{
+ free(store);
+ store=strdup(line);
+ return store!=0;
+}
+
+bool cLineDummy::Save(FILE *f)
+{
+ fprintf(f,"%s",store);
+ return ferror(f)==0;
+}
+
+// -- cLoaderDummy -------------------------------------------------------------
+
+class cLoaderDummy : public cSimpleList<cLineDummy>, public cLoader {
+public:
+ cLoaderDummy(const char *Id);
+ virtual bool ParseLine(const char *line, bool fromCache);
+ virtual bool Save(FILE *f);
+ };
+
+cLoaderDummy::cLoaderDummy(const char *id)
+:cLoader(id)
+{}
+
+bool cLoaderDummy::ParseLine(const char *line, bool fromCache)
+{
+ if(fromCache) {
+ cLineDummy *k=new cLineDummy;
+ if(k) {
+ if(k->Parse(line)) Add(k);
+ else delete k;
+ return true;
+ }
+ PRINTF(L_GEN_ERROR,"not enough memory for %s loader dummy!",Id());
+ }
+ return false;
+}
+
+bool cLoaderDummy::Save(FILE *f)
+{
+ bool res=true;
+ for(cLineDummy *k=First(); k; k=Next(k))
+ if(!k->Save(f)) { res=false; break; }
+ Modified(!res);
+ return res;
+}
+
+// -- cLoader ------------------------------------------------------------------
+
+cLoader::cLoader(const char *Id)
+{
+ id=Id; modified=false;
+ cLoaders::Register(this);
+}
+
+// -- cLoaders -----------------------------------------------------------------
+
+cLoader *cLoaders::first=0;
+cMutex cLoaders::lock;
+char *cLoaders::cacheFile=0;
+
+void cLoaders::Register(cLoader *ld)
+{
+ PRINTF(L_CORE_DYN,"loaders: registering loader %s",ld->id);
+ ld->next=first;
+ first=ld;
+}
+
+void cLoaders::LoadCache(const char *cfgdir)
+{
+ lock.Lock();
+ cacheFile=strdup(AddDirectory(cfgdir,CACACHE_FILE));
+ if(access(cacheFile,F_OK)==0) {
+ PRINTF(L_GEN_INFO,"loading ca cache from %s",cacheFile);
+ FILE *f=fopen(cacheFile,"r");
+ if(f) {
+ char buf[512];
+ cLoader *ld=0;
+ while(fgets(buf,sizeof(buf),f)) {
+ if(!index(buf,'\n'))
+ PRINTF(L_GEN_ERROR,"loaders fgets readbuffer overflow");
+ if(buf[0]=='#') continue;
+ if(!strncmp(buf,":::",3)) { // new loader section
+ ld=FindLoader(stripspace(&buf[3]));
+ if(!ld) {
+ PRINTF(L_CORE_LOAD,"unknown loader section '%s', adding dummy",&buf[3]);
+ ld=new cLoaderDummy(strdup(&buf[3]));
+ }
+ }
+ else if(ld) {
+ if(!ld->ParseLine(buf,true)) {
+ PRINTF(L_CORE_LOAD,"loader '%s' failed on line '%s'",ld->Id(),buf);
+ }
+ }
+ }
+ fclose(f);
+ }
+ else LOG_ERROR_STR(cacheFile);
+ }
+ lock.Unlock();
+}
+
+void cLoaders::SaveCache(void)
+{
+ lock.Lock();
+ if(cacheFile && IsModified()) {
+ cSafeFile f(cacheFile);
+ if(f.Open()) {
+ fprintf(f,"## This is a generated file. DO NOT EDIT!!\n"
+ "## This file will be OVERWRITTEN WITHOUT WARNING!!\n");
+
+ cLoader *ld=first;
+ while(ld) {
+ fprintf(f,":::%s\n",ld->Id());
+ if(!ld->Save(f)) break;
+ ld=ld->next;
+ }
+ f.Close();
+ PRINTF(L_CORE_LOAD,"saved cache to file");
+ }
+ }
+ lock.Unlock();
+}
+
+bool cLoaders::IsModified(void)
+{
+ bool res=false;
+ lock.Lock();
+ cLoader *ld=first;
+ while(ld) {
+ if(ld->IsModified()) {
+ res=true; break;
+ }
+ ld=ld->next;
+ }
+ lock.Unlock();
+ return res;
+}
+
+cLoader *cLoaders::FindLoader(const char *id)
+{
+ lock.Lock();
+ cLoader *ld=first;
+ while(ld) {
+ if(!strcmp(id,ld->Id())) break;
+ ld=ld->next;
+ }
+ lock.Unlock();
+ return ld;
+}
+
+// -- cPid ---------------------------------------------------------------------
+
+cPid::cPid(int Pid, int Section, int Mask, int Mode)
+{
+ pid=Pid;
+ sct=Section;
+ mask=Mask;
+ mode=Mode;
+ filter=0;
+}
+
+// -- cPids --------------------------------------------------------------------
+
+void cPids::AddPid(int Pid, int Section, int Mask, int Mode)
+{
+ if(!HasPid(Pid,Section,Mask,Mode)) {
+ cPid *pid=new cPid(Pid,Section,Mask,Mode);
+ Add(pid);
+ }
+}
+
+bool cPids::HasPid(int Pid, int Section, int Mask, int Mode)
+{
+ for(cPid *pid=First(); pid; pid=Next(pid))
+ if(pid->pid==Pid && pid->sct==Section && pid->mask==Mask && pid->mode==Mode)
+ return true;
+ return false;
+}
+
+// -- cEcmInfo -----------------------------------------------------------------
+
+cEcmInfo::cEcmInfo(void)
+{
+ Setup();
+}
+
+cEcmInfo::cEcmInfo(const cEcmInfo *e)
+{
+ Setup(); SetName(e->name);
+ ecm_pid=e->ecm_pid;
+ ecm_table=e->ecm_table;
+ caId=e->caId;
+ emmCaId=e->emmCaId;
+ provId=e->provId;
+ Update(e);
+ prgId=e->prgId;
+ source=e->source;
+ transponder=e->transponder;
+}
+
+cEcmInfo::cEcmInfo(const char *Name, int Pid, int CaId, int ProvId)
+{
+ Setup(); SetName(Name);
+ ecm_pid=Pid;
+ caId=CaId;
+ provId=ProvId;
+}
+
+cEcmInfo::~cEcmInfo()
+{
+ ClearData();
+ free(name);
+}
+
+void cEcmInfo::Setup(void)
+{
+ cached=failed=false;
+ name=0; data=0;
+ prgId=source=transponder=-1;
+ ecm_table=0x80; emmCaId=0;
+}
+
+bool cEcmInfo::Compare(const cEcmInfo *e)
+{
+ return prgId==e->prgId && source==e->source && transponder==e->transponder &&
+ caId==e->caId && ecm_pid==e->ecm_pid && provId==e->provId;
+}
+
+bool cEcmInfo::Update(const cEcmInfo *e)
+{
+ return (e->data && (!data || e->dataLen!=dataLen)) ? AddData(e->data,e->dataLen) : false;
+}
+
+void cEcmInfo::SetSource(int PrgId, int Source, int Transponder)
+{
+ prgId=PrgId;
+ source=Source;
+ transponder=Transponder;
+}
+
+void cEcmInfo::ClearData(void)
+{
+ free(data); data=0;
+}
+
+bool cEcmInfo::AddData(const unsigned char *Data, int DataLen)
+{
+ ClearData();
+ data=MALLOC(unsigned char,DataLen);
+ if(data) {
+ memcpy(data,Data,DataLen);
+ dataLen=DataLen;
+ }
+ else PRINTF(L_GEN_ERROR,"malloc failed in cEcmInfo::AddData()");
+ return (data!=0);
+}
+
+void cEcmInfo::SetName(const char *Name)
+{
+ free(name);
+ name=strdup(Name);
+}
+
+// -- cPlainKey ----------------------------------------------------------------
+
+cPlainKey::cPlainKey(bool CanSupersede)
+{
+ au=del=false;
+ super=CanSupersede;
+}
+
+bool cPlainKey::Set(int Type, int Id, int Keynr, void *Key, int Keylen)
+{
+ type=Type; id=Id; keynr=Keynr;
+ return SetKey(Key,Keylen);
+}
+
+cString cPlainKey::PrintKeyNr(void)
+{
+ return cString::sprintf("%02X",keynr);
+}
+
+int cPlainKey::IdSize(void)
+{
+ return id>0xFF ? (id>0xFFFF ? 6 : 4) : 2;
+}
+
+bool cPlainKey::Save(FILE *f)
+{
+ fprintf(f,"%s\n",*ToString(false));
+ return ferror(f)==0;
+}
+
+cString cPlainKey::ToString(bool hide)
+{
+ return cString::sprintf(hide ? "%c %.*X %s %.4s..." : "%c %.*X %s %s",type,IdSize(),id,*PrintKeyNr(),*Print());
+}
+
+void cPlainKey::FormatError(const char *type, const char *sline)
+{
+ PRINTF(L_GEN_WARN,"%s key: bad format '%.15s%s'\n",type,sline,(strlen(sline)>15)?"...":"");
+}
+
+// -- cMutableKey --------------------------------------------------------------
+
+cMutableKey::cMutableKey(bool Super)
+:cPlainKey(Super)
+{
+ real=0;
+}
+
+cMutableKey::~cMutableKey()
+{
+ delete real;
+}
+
+bool cMutableKey::SetKey(void *Key, int Keylen)
+{
+ delete real;
+ return (real=Alloc()) && real->SetKey(Key,Keylen);
+}
+
+bool cMutableKey::SetBinKey(unsigned char *Mem, int Keylen)
+{
+ delete real;
+ return (real=Alloc()) && real->SetBinKey(Mem,Keylen);
+}
+
+int cMutableKey::Size(void)
+{
+ return real->Size();
+}
+
+bool cMutableKey::Cmp(void *Key, int Keylen)
+{
+ return real->Cmp(Key,Keylen);
+}
+
+bool cMutableKey::Cmp(cPlainKey *k)
+{
+ cMutableKey *mk=dynamic_cast<cMutableKey *>(k); // high magic ;)
+ return real->Cmp(mk ? mk->real : k);
+}
+
+void cMutableKey::Get(void *mem)
+{
+ real->Get(mem);
+}
+
+cString cMutableKey::Print(void)
+{
+ return real->Print();
+}
+
+// ----------------------------------------------------------------
+
+class cPlainKeyDummy : public cPlainKey {
+private:
+ char *str;
+ int len;
+protected:
+ virtual cString Print(void);
+ virtual cString PrintKeyNr(void) { return ""; }
+public:
+ cPlainKeyDummy(void);
+ ~cPlainKeyDummy();
+ virtual bool Parse(const char *line);
+ virtual bool Cmp(void *Key, int Keylen) { return false; }
+ virtual bool Cmp(cPlainKey *k) { return false; }
+ virtual void Get(void *mem) {}
+ virtual int Size(void) { return len; }
+ virtual bool SetKey(void *Key, int Keylen);
+ virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+ };
+
+cPlainKeyDummy::cPlainKeyDummy(void):
+cPlainKey(false)
+{
+ str=0;
+}
+
+cPlainKeyDummy::~cPlainKeyDummy()
+{
+ free(str);
+}
+
+bool cPlainKeyDummy::SetKey(void *Key, int Keylen)
+{
+ len=Keylen;
+ free(str);
+ str=MALLOC(char,len+1);
+ if(str) strn0cpy(str,(char *)Key,len+1);
+ return str!=0;
+}
+
+bool cPlainKeyDummy::SetBinKey(unsigned char *Mem, int Keylen)
+{
+ return SetKey(Mem,Keylen);
+}
+
+cString cPlainKeyDummy::Print(void)
+{
+ return str;
+}
+
+bool cPlainKeyDummy::Parse(const char *line)
+{
+ unsigned char sid[3];
+ int len;
+ if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false))) {
+ type=toupper(type); id=Bin2Int(sid,len);
+ char *l=strdup(line);
+ line=skipspace(stripspace(l));
+ SetKey((void *)line,strlen(line));
+ free(l);
+ return true;
+ }
+ return false;
+}
+
+// -- cPlainKeyTypeDummy -------------------------------------------------------
+
+class cPlainKeyTypeDummy : public cPlainKeyType {
+public:
+ cPlainKeyTypeDummy(int Type):cPlainKeyType(Type,false) {}
+ virtual cPlainKey *Create(void) { return new cPlainKeyDummy; }
+ };
+
+// -- cPlainKeyType ------------------------------------------------------------
+
+cPlainKeyType::cPlainKeyType(int Type, bool Super)
+{
+ type=Type;
+ cPlainKeys::Register(this,Super);
+}
+
+// -- cPlainKeys ---------------------------------------------------------------
+
+const char *externalAU=0;
+
+cPlainKeys keys;
+
+cPlainKeyType *cPlainKeys::first=0;
+
+cPlainKeys::cPlainKeys(void)
+:cLoader("KEY")
+//,cThread("ExternalAU")
+{
+ mark=0;
+}
+
+void cPlainKeys::Register(cPlainKeyType *pkt, bool Super)
+{
+ PRINTF(L_CORE_DYN,"registering key type %c%s",pkt->type,Super?" (super)":"");
+ pkt->next=first;
+ first=pkt;
+}
+
+cPlainKey *cPlainKeys::FindKey(int Type, int Id, int Keynr, int Size, cPlainKey *key)
+{
+ key=FindKeyNoTrig(Type,Id,Keynr,Size,key);
+ if(!key) {
+ static int lastType=-1, lastId=-1, lastKeynr=-1;
+ if(externalAU && (lastType!=Type || lastId!=Id || lastKeynr!=Keynr)) {
+ PRINTF(L_CORE_AUEXTERN,"triggered from findkey (type=%X id=%X keynr=%X)",Type,Id,Keynr);
+ lastType=Type; lastId=Id; lastKeynr=Keynr;
+ }
+ ExternalUpdate();
+ }
+ return key;
+}
+
+cPlainKey *cPlainKeys::FindKeyNoTrig(int Type, int Id, int Keynr, int Size, cPlainKey *key)
+{
+ Lock();
+ if(key) key=Next(key); else key=First();
+ while(key) {
+ if(!key->IsInvalid() && key->type==Type && key->id==Id && key->keynr==Keynr && (Size<0 || key->Size()==Size)) break;
+ key=Next(key);
+ }
+ Unlock();
+ return key;
+}
+
+bool cPlainKeys::NewKey(int Type, int Id, int Keynr, void *Key, int Keylen)
+{
+ cPlainKey *k=0;
+ while((k=FindKeyNoTrig(Type,Id,Keynr,-1,k)))
+ if(k->Cmp(Key,Keylen)) return false;
+
+ cPlainKey *nk=NewFromType(Type);
+ if(nk) {
+ nk->Set(Type,Id,Keynr,Key,Keylen);
+ AddNewKey(nk,2,true);
+ return true;
+ }
+ else PRINTF(L_GEN_ERROR,"no memory for new key ID %c %.2x!",Type,Id);
+ return false;
+}
+
+void cPlainKeys::AddNewKey(cPlainKey *nk, int mode, bool log)
+{
+ if(mode>=1) {
+ nk->SetAuto();
+ if(log) PRINTF(L_GEN_INFO,"key update for ID %s",*nk->ToString(true));
+ if(nk->CanSupersede()) {
+ cPlainKey *k=0;
+ while((k=FindKeyNoTrig(nk->type,nk->id,nk->keynr,nk->Size(),k))) {
+ if(!k->IsInvalid()) {
+ k->SetInvalid();
+ if(k->IsAuto()) Modified();
+ if(log) PRINTF(L_GEN_INFO,"supersedes key: %s%s",*k->ToString(true),k->IsAuto()?" (auto)":"");
+ }
+ }
+ }
+ }
+ Lock();
+ switch(mode) {
+ case 0: Add(nk); break;
+ case 1: if(!mark) Ins(nk); else Add(nk,mark);
+ mark=nk;
+ break;
+ case 2: Ins(nk); Modified(); break;
+ }
+ Unlock();
+}
+
+bool cPlainKeys::Load(const char *cfgdir)
+{
+ Lock();
+ Clear(); mark=0;
+ cString cname=AddDirectory(cfgdir,KEY_FILE);
+ ConfRead("keys",cname);
+ int n=Count();
+ PRINTF(L_CORE_LOAD,"loaded %d keys from %s",n,*cname);
+ if(n && LOG(L_CORE_KEYS)) {
+ cPlainKey *dat=First();
+ while(dat) {
+ if(!dat->IsInvalid()) PRINTF(L_CORE_KEYS,"keys %s",*dat->ToString(false));
+ dat=Next(dat);
+ }
+ }
+ Unlock();
+ return (n!=0);
+}
+
+void cPlainKeys::HouseKeeping(void)
+{
+ cLoaders::SaveCache();
+ if(trigger.TimedOut()) {
+ trigger.Set(EXT_AU_INT);
+ if(externalAU) PRINTF(L_CORE_AUEXTERN,"triggered from housekeeping");
+ ExternalUpdate();
+ }
+}
+
+void cPlainKeys::ExternalUpdate(void)
+{
+ if(externalAU && ScSetup.AutoUpdate>0) {
+ if(last.TimedOut()) {
+ Lock();
+ if(!Active())
+ Start();
+ else PRINTF(L_CORE_AUEXTERN,"still running");
+ Unlock();
+ }
+ else PRINTF(L_CORE_AUEXTERN,"denied, min. timeout not expired");
+ }
+}
+
+void cPlainKeys::Action(void)
+{
+ last.Set(EXT_AU_MIN);
+ PRINTF(L_CORE_AUEXTERN,"starting...");
+ cTimeMs start;
+ cPipe pipe;
+ if(pipe.Open(externalAU,"r")) {
+ char buff[1024];
+ while(fgets(buff,sizeof(buff),pipe)) {
+ char *line=skipspace(stripspace(buff));
+ if(line[0]==0 || line[0]==';' || line[0]=='#') continue;
+ cPlainKey *nk=NewFromType(toupper(line[0]));
+ if(nk && nk->Parse(line)) {
+ cPlainKey *k=0;
+ while((k=FindKeyNoTrig(nk->type,nk->id,nk->keynr,-1,k)))
+ if(k->Cmp(nk)) break;
+ if(!k) {
+ AddNewKey(nk,2,true);
+ nk=0;
+ }
+ }
+ delete nk;
+ }
+ }
+ pipe.Close();
+ PRINTF(L_CORE_AUEXTERN,"done (elapsed %d)",(int)start.Elapsed());
+}
+
+cPlainKey *cPlainKeys::NewFromType(int type)
+{
+ cPlainKeyType *pkt=first;
+ while(pkt) {
+ if(pkt->type==type) return pkt->Create();
+ pkt=pkt->next;
+ }
+ PRINTF(L_CORE_LOAD,"unknown key type '%c', adding dummy",type);
+ pkt=new cPlainKeyTypeDummy(type);
+ return pkt->Create();
+}
+
+bool cPlainKeys::ParseLine(const char *line, bool fromCache)
+{
+ char *s=skipspace(line);
+ cPlainKey *k=NewFromType(toupper(*s));
+ if(k) {
+ if(k->Parse((char *)line)) AddNewKey(k,fromCache?1:0,false);
+ else delete k;
+ return true;
+ }
+ return false;
+}
+
+bool cPlainKeys::Save(FILE *f)
+{
+ bool res=true;
+ Lock();
+ cPlainKey *dat=First();
+ while(dat) {
+ if(dat->IsAuto() && !dat->IsInvalid()) {
+ if(!dat->Save(f)) { res=false; break; }
+ }
+ dat=Next(dat);
+ }
+ Modified(!res);
+ Unlock();
+ return res;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___DATA_H
+#define ___DATA_H
+
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+#include "misc.h"
+
+class cLoaders;
+class cPidFilter;
+class cPlainKeys;
+
+// ----------------------------------------------------------------
+
+class cFileMap : public cSimpleItem, private cMutex {
+private:
+ char *filename;
+ bool rw;
+ //
+ int fd, len, count;
+ unsigned char *addr;
+ bool failed;
+ //
+ void Clean(void);
+public:
+ cFileMap(const char *Filename, bool Rw);
+ ~cFileMap();
+ bool Map(void);
+ bool Unmap(void);
+ void Sync(void);
+ int Size(void) const { return len; }
+ unsigned char *Addr(void) const { return addr; }
+ bool IsFileMap(const char *Name, bool Rw);
+ };
+
+// ----------------------------------------------------------------
+
+class cFileMaps : public cSimpleList<cFileMap>, private cMutex {
+private:
+ char *cfgDir;
+public:
+ cFileMaps(void);
+ ~cFileMaps();
+ void SetCfgDir(const char *CfgDir);
+ cFileMap *GetFileMap(const char *name, const char *domain, bool rw);
+ };
+
+extern cFileMaps filemaps;
+
+// ----------------------------------------------------------------
+
+class cConfRead {
+public:
+ virtual ~cConfRead() {}
+ bool ConfRead(const char *type, const char *filename);
+ virtual bool ParseLine(const char *line, bool fromCache)=0;
+ };
+
+// ----------------------------------------------------------------
+
+class cLoader {
+friend class cLoaders;
+private:
+ cLoader *next;
+ bool modified;
+ const char *id;
+protected:
+ void Modified(bool mod=true) { modified=mod; }
+public:
+ cLoader(const char *Id);
+ virtual ~cLoader() {}
+ virtual bool ParseLine(const char *line, bool fromCache)=0;
+ virtual bool Save(FILE *f)=0;
+ bool IsModified(void) const { return modified; }
+ const char *Id(void) const { return id; }
+ };
+
+// ----------------------------------------------------------------
+
+class cLoaders {
+friend class cLoader;
+private:
+ static cLoader *first;
+ static cMutex lock;
+ static char *cacheFile;
+ //
+ static void Register(cLoader *ld);
+ static cLoader *FindLoader(const char *id);
+ static bool IsModified(void);
+public:
+ static void LoadCache(const char *cfgdir);
+ static void SaveCache(void);
+ };
+
+// ----------------------------------------------------------------
+
+class cPid : public cSimpleItem {
+public:
+ int pid, sct, mask, mode;
+ cPidFilter *filter;
+ //
+ cPid(int Pid, int Section, int Mask, int Mode);
+ };
+
+// ----------------------------------------------------------------
+
+class cPids : public cSimpleList<cPid> {
+public:
+ void AddPid(int Pid, int Section, int Mask, int Mode=0);
+ bool HasPid(int Pid, int Section, int Mask, int Mode=0);
+ };
+
+// ----------------------------------------------------------------
+
+class cEcmInfo : public cSimpleItem {
+private:
+ bool cached, failed;
+ //
+ void Setup(void);
+protected:
+ int dataLen;
+ unsigned char *data;
+ //
+ void ClearData(void);
+public:
+ char *name;
+ int ecm_pid, ecm_table;
+ int caId, provId, emmCaId;
+ int prgId, source, transponder;
+ //
+ cEcmInfo(void);
+ cEcmInfo(const cEcmInfo *e);
+ cEcmInfo(const char *Name, int Pid, int CaId, int ProvId);
+ ~cEcmInfo();
+ bool Compare(const cEcmInfo *e);
+ bool Update(const cEcmInfo *e);
+ void SetSource(int PrgId, int Source, int Transponder);
+ void SetName(const char *Name);
+ bool AddData(const unsigned char *Data, int DataLen);
+ const unsigned char *Data(void) const { return data; }
+ void Fail(bool st) { failed=st; }
+ bool Failed(void) const { return failed; }
+ void SetCached(void) { cached=true; }
+ bool Cached(void) const { return cached; }
+ };
+
+// ----------------------------------------------------------------
+
+class cMutableKey;
+
+class cPlainKey : public cSimpleItem {
+friend class cPlainKeys;
+friend class cMutableKey;
+private:
+ bool au, del, super;
+protected:
+ void SetInvalid(void) { del=true; }
+ void SetAuto(void) { au=true; }
+ bool IsAuto(void) const { return au; }
+ bool IsInvalid(void) const { return del; }
+ void SetSupersede(bool val) { super=val; }
+ bool CanSupersede(void) const { return super; }
+ virtual int IdSize(void);
+ virtual cString Print(void)=0;
+ virtual cString PrintKeyNr(void);
+ void FormatError(const char *type, const char *sline);
+public:
+ int type, id, keynr;
+ //
+ cPlainKey(bool CanSupersede);
+ virtual bool Parse(const char *line)=0;
+ bool Save(FILE *f);
+ cString ToString(bool hide=false);
+ virtual bool Cmp(void *Key, int Keylen)=0;
+ virtual bool Cmp(cPlainKey *k)=0;
+ virtual void Get(void *mem)=0;
+ virtual int Size(void)=0;
+ bool Set(int Type, int Id, int Keynr, void *Key, int Keylen);
+ virtual bool SetKey(void *Key, int Keylen)=0;
+ virtual bool SetBinKey(unsigned char *Mem, int Keylen)=0;
+ };
+
+// ----------------------------------------------------------------
+
+class cMutableKey : public cPlainKey {
+private:
+ cPlainKey *real;
+protected:
+ virtual cString Print(void);
+ virtual cPlainKey *Alloc(void) const=0;
+public:
+ cMutableKey(bool Super);
+ virtual ~cMutableKey();
+ virtual bool Cmp(void *Key, int Keylen);
+ virtual bool Cmp(cPlainKey *k);
+ virtual void Get(void *mem);
+ virtual int Size(void);
+ virtual bool SetKey(void *Key, int Keylen);
+ virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+ };
+
+// ----------------------------------------------------------------
+
+class cPlainKeyType {
+friend class cPlainKeys;
+private:
+ int type;
+ cPlainKeyType *next;
+public:
+ cPlainKeyType(int Type, bool Super);
+ virtual ~cPlainKeyType() {}
+ virtual cPlainKey *Create(void)=0;
+ };
+
+// ----------------------------------------------------------------
+
+template<class PKT, int KT, bool SUP=true> class cPlainKeyTypeReg : public cPlainKeyType {
+public:
+ cPlainKeyTypeReg(void):cPlainKeyType(KT,SUP) {}
+ virtual cPlainKey *Create(void) { return new PKT(SUP); }
+ };
+
+// ----------------------------------------------------------------
+
+extern const char *externalAU;
+
+class cPlainKeys : public cLoader, private cConfRead, private cThread, public cSimpleList<cPlainKey> {
+friend class cPlainKeyType;
+private:
+ static cPlainKeyType *first;
+ cPlainKey *mark;
+ cTimeMs trigger, last;
+ //
+ static void Register(cPlainKeyType *pkt, bool Super);
+ cPlainKey *NewFromType(int type);
+ void AddNewKey(cPlainKey *nk, int mode, bool log);
+ void ExternalUpdate(void);
+protected:
+ virtual void Action(void);
+public:
+ cPlainKeys(void);
+ bool Load(const char *cfgdir);
+ virtual bool Save(FILE *f);
+ virtual bool ParseLine(const char *line, bool Au);
+ cPlainKey *FindKey(int Type, int Id, int Keynr, int Size, cPlainKey *key=0);
+ cPlainKey *FindKeyNoTrig(int Type, int Id, int Keynr, int Size, cPlainKey *key=0);
+ bool NewKey(int Type, int Id, int Keynr, void *Key, int Keylen);
+ void HouseKeeping(void);
+ };
+
+extern cPlainKeys keys;
+
+#endif //___DATA_H
--- /dev/null
+patches
+systems-pre
+.dependencies
+diff.exclude
+*.so
+*.a
+*.o
+*.save
+*.orig
+*.rej
+*.bak
+nagra2-prov.c
--- /dev/null
+; Irdeto/Betacrypt cards\r
+;\r
+; AAAAAA BBBBBBBBBBBBBBBBBBBB CC DDDDDD EEEEEEEEEEEEEEEE\r
+;\r
+; AAAAAA - HS hex serial\r
+; BBBBBBBBBBBBBBBBBBBB - HMK hex master key\r
+; CC - provider\r
+; DDDDDD - provider ID\r
+; EEEEEEEEEEEEEEEE - PMK plain master key\r
+;\r
+123456 12345612345612345612 00 123456 1234567890123456 ; dummy\r
+\r
--- /dev/null
+; Seca cards\r
+;\r
+; AAAA BBBBBBBB CCCCCCCCCCCCCCCC\r
+;\r
+; AAAA - provider ident\r
+; BBBBBB - shared address\r
+; CCCCCCCCCCCCCCCC - key 01\r
+;\r
+1234 123456 1234561234561234 ; dummy\r
--- /dev/null
+;
+; Irdeto/Betacrypt
+;
+; I XX YY <16 characters>
+;
+; XX - provider
+; YY - key number
+;
+
+;
+; Seca
+;
+; S XXXX YY <16 characters/32 characters>
+; S XXXX [EMM] Mz <180 characters>
+; S XXXX [EMM] Ez <12 characters/180 characters>
+; S XXXX N51 M9 <258 characters>
+; S XXXX N51 E9 <2 characters>
+;
+; XXXX - provider ident
+; YY - key number
+; Mz / Ez - RSA key name (z = 1,3,5).
+; Exponent keys may be padded up to 180 chars.
+; Optional "EMM" for EMM RSA keys.
+; N51 - RSA keys for Nano 5109 handling.
+;
+
+;
+; Viaccess
+;
+; V XXXXXX YY <16 characters/32 characters>
+; V XXXXXX TPS <32 characters> ; old TPS keys. no longer used
+; V XXXXXX TPSMKy <32 characters> ; TPS masterkeys for AU y=0-7
+;
+; XXXXXX - provider ident
+; YY - key number
+;
+
+;
+; Nagra 1
+;
+; N XXXX YY <16 characters>
+; N XXXX E1 [ROMq] [PKv] [TYPw] <128 characters>
+; N XXXX N1 [ROMq] [PKv] [TYPw] <128 characters>
+; N XXXX N2 [ROMq] [TYPw] <128 characters>
+; N XXXX V [ROMq] [TYPw] <16 characters>
+;
+; Nagra 2
+;
+; N XXXX YY <32 characters> ; IDEA opkey (00/01)
+; N XXXX V <32 characters> ; IDEA verify key
+; N XXXX M1 <128 characters> ; RSA key
+; N XXXX NN ZZ <32/48/192 characters> ; EMM keys (literal 'NN'), some examples:
+; N XXXX NN 02 <32/48 characters> ; EMM-G IDEA key (former N XXXX 02)
+; N XXXX NN 03 <32 characters> ; EMM-G signature key
+; N XXXX NN 12 <192 characters> ; EMM-G RSA key (former N XXXX N2)
+; N XXXX NN 30 <32 characters> ; EMM-G 3DES key
+;
+; XXXX - provider ident
+; YY - key number
+; ZZ - EEPROM key number
+; ROMq - (optional) ROM specific keys (q = ROM number)
+; PKv - (optional) PK key number (v = 0,1,2)
+; TYPw - (optional) key type number (w = 0,1)
+;
+
+;
+; Conax
+;
+; C XX M <128 characters>
+; C XX E <128 characters>
+;
+; XX - key number
+;
+
+;
+; Cryptoworks
+;
+; W XXXX YY ZZ VV <32 characters> ; opkey
+; W XXXX YY ZZ VV <128 characters> ; RSA key (normaly ZZ=10)
+; W XXXX YY CC <12 characters> ; cardkey (literal 'CC')
+;
+; XXXX - caid
+; YY - provider ident
+; ZZ - keytype (20/31/10 derived from cardfiles EF20/EF31/EF10)
+; VV - keyid (00/01)
+;
+
+;
+; @SHL (SkyCrypt)
+;
+; Z 00 00 <16 characters>
+;
+
+;
+; Constant CW
+;
+; X YYYY freq:pol:src:sid <32 characters>
+;
+; YYYY - CA system id e.g. 0100
+; freq - frequency
+; pol - polarisation (v/h/r/l)
+; src - source
+; sid - service ID
+;
+; example:
+X 0d02 12670:v:S19.2E:23457 00000000000000000000000000000000
+; cable example (Z can be an arbitrary letter, is ignored)
+X 1801 346:Z:C:23457 00000000000000000000000000000000
--- /dev/null
+; Viaccess cards
+;
+; AAAAAA BBBBBBBBBB CC DDDDDDDDDDDDDDDD
+;
+; AAAAAA - Provider ident
+; BBBBBBBBBB - Unique address (UA)
+; SSSSSSSS - Shared address (SA)
+; CC - Management key number
+; DDDDDDDDDDDDDDDD - Management key
+;
+123456 1234567890 12345678 01 1234561234561234 ; Dummy
--- /dev/null
+#
+# Comment lines can start with # or ;
+#
+# every client line starts with the client name, followed by some arguments:
+# 'hostname' is the name of the server
+# 'port' is the port on the server
+# 'emm' is a flag to allow EMM transfers to the server
+# (0=disabled 1=enabled)
+# 'caid' (optional) caid on which this client should work
+# 'mask' (optional) mask for caid e.g. caid=1700 mask=FF00 would allow
+# anything between 1700 & 17FF.
+# Default is 1700 & FF00. If only caid is given mask is FFFF.
+# You may give multiple caid/mask values comma separated
+# (e.g. 1702,1722,0d0c/ff00).
+# 'username' is the login username
+# 'password' is the login password
+#
+# radegast client
+radegast:hostname:port:emm/caid/mask
+#
+# aroureos client
+# 'hexbase'
+# 'hexserial' card data for which EMM updates should be send
+aroureos:hostname:port:emm/caid/mask:hexbase:hexserial
+#
+# camd33 client (tcp protocol)
+# 'aeskey' is the AES key (32bytes), disable encryption if missing
+camd33:hostname:port:emm/caid/mask:username:password:aeskey
+#
+# camd35 client (udp protocol)
+camd35:hostname:port:emm/caid/mask:username:password
+#
+# cardd client
+cardd:hostname:port:emm/caid/mask:username:password
+#
+# buffy client
+# 'aeskey' is the AES key (32bytes), disable encryption if missing
+buffy:hostname:port:emm:username:password:aeskey
+#
+# newcamd client
+# 'cfgkey' is the config key (28bytes)
+newcamd:hostname:port:emm/caid/mask:username:password:cfgKey
+#
+# gbox client
+#
+# NOTE: hostname & port will be ignore. GBOX must be runnning on the local
+# machine. For convinience you should choose localhost:8004
+gbox:hostname:port:emm/caid/mask
--- /dev/null
+#!/bin/bash
+#
+# This script is called from VDR to start/stop dialup-network
+#
+# argument 1: wanted action, one of start,stop
+#
+
+RUN="/var/tmp/vdr.dialup.run"
+
+action="$1"
+
+case "$action" in
+up)
+ up ppp0
+ touch "$RUN"
+ sleep 2
+ ;;
+down)
+ down ppp0
+ rm -f "$RUN"
+ ;;
+esac
+
+exit 0
--- /dev/null
+#!/bin/bash
+
+URL="http://www.some-host.com/aes/"
+
+key=`wget -q -O- -U "Mozilla" "--referer=$URL" "$URL" | sed -ne '/007C00/ s/^.*clef Pri//p' | sed -ne 's/<pre>.*$//p' | sed -ne 's/: [0-9]\{2\} ://gp' | sed -ne 's/[^0-9A-F]//gp'`
+if [ "aa$key" != "aa" ]; then
+ echo "V 007C00 TPS $key"
+fi
--- /dev/null
+;
+; Comment lines can start with # or ;
+;
+
+; Nagra
+;
+; boxkey and modulus for camcrypt
+;
+; prvid - provider ID (2 byte = 4 chars)
+; boxkey - boxkey (8 byte = 16 chars)
+; mod - modulus (64 byte = 128 chars)
+;
+; If IRDMOD is present, the given mod is used to decrypt the cammod from DT08
+; data. If IRDMOD is obmited, the given mod is used as cammod directly
+;
+; nagra: [prvid] IRDMOD boxkey mod
+nagra: [0501] IRDMOD 0011223344556677 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+;
+; nagra: [prvid] boxkey mod
+nagra: [1201] 0011223344556677 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+
+; Cryptoworks
+;
+; RSA modulus for camcrypt
+;
+; caid - caid identifying the provider
+; serial - serial number identifying the card
+; ipk - issuer public key (IPK), 64 bytes = 128 chars
+; ucpk - session key (calculated from ISK, IPK & exponent), 64 bytes = 128 chars
+; pin - pin number to disable parental rating
+;
+; Note: you need only one of them for your caid. IPK is more general (common for
+; all cards of a provider), while the UCPK is different for every single card.
+;
+; cryptoworks: IPK caid ipk
+cryptoworks: IPK 0d22 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+;
+; cryptoworks: UCPK serial ucpk
+cryptoworks: UCPK 0102030405 00112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233
+;
+; cryptoworks: PIN serial pin
+cryptoworks: PIN 0102030405 1234
+
+;
+; Irdeto/Betacrypt
+;
+; RSA certificate for camkey challenge
+;
+; acs - ACS version identifying the card (optional)
+; caid - caid for further identification of the card (optional)
+; mod - RSA modulus, usualy 128 bytes = 256 chars
+; exp - RSA exponent, usualy 1 byte = 2 chars
+;
+; Irdeto default certificate
+; irdeto: mod exp
+irdeto: 0011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677 03
+;
+; a ACS specific certificate
+; irdeto: [acs] mod exp
+irdeto: [0604] 0011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677 03
+;
+; a ACS/caid specific certificate
+; irdeto: [acs/caid] mod exp
+irdeto: [0384/1722] 0011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677 03
+;
+; indicates that a card doesn't want/need RSA camkey exchange
+; NOTE: this is autodetected for ACS 0383/0384 cards (Z/non-Z mode)
+;
+; irdeto: [acs/caid] PLAIN
+irdeto: [0605/0622] PLAIN
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <unistd.h>
+
+#include <linux/dvb/dmx.h>
+
+#include <vdr/tools.h>
+
+#include "filter.h"
+#include "misc.h"
+#include "log-core.h"
+
+// -- cPidFilter ------------------------------------------------------------------
+
+cPidFilter::cPidFilter(const char *Id, int Num, int DvbNum, unsigned int IdleTime)
+{
+ dvbNum=DvbNum;
+ idleTime=IdleTime;
+ id=0; fd=-1; active=false; forceRun=false; userData=0;
+ asprintf(&id,"%s/%d",Id,Num);
+ PRINTF(L_CORE_ACTION,"new filter '%s' on card %d (%d ms)",id,dvbNum,idleTime);
+}
+
+cPidFilter::~cPidFilter()
+{
+ cMutexLock lock(this);
+ if(fd>=0) {
+ Stop();
+ close(fd);
+ }
+ PRINTF(L_CORE_ACTION,"filter '%s' on card %d removed",id,dvbNum);
+ free(id);
+}
+
+bool cPidFilter::Open(void)
+{
+ cMutexLock lock(this);
+ if(fd<0) {
+ fd=DvbOpen(DEV_DVB_DEMUX,dvbNum,O_RDWR|O_NONBLOCK);
+ if(fd>=0) return true;
+ }
+ return false;
+}
+
+void cPidFilter::Flush(void)
+{
+ cMutexLock lock(this);
+ if(fd>=0) {
+ unsigned char buff[MAX_SECT_SIZE];
+ while(read(fd,buff,sizeof(buff))>0);
+ }
+}
+
+void cPidFilter::Start(int Pid, int Section, int Mask, int Mode, bool Crc)
+{
+ cMutexLock lock(this);
+ if(fd>=0) {
+ Stop();
+ struct dmx_sct_filter_params FilterParams;
+ memset(&FilterParams,0,sizeof(FilterParams));
+ FilterParams.filter.filter[0]=Section;
+ FilterParams.filter.mask[0]=Mask;
+ FilterParams.filter.mode[0]=Mode;
+ FilterParams.flags=DMX_IMMEDIATE_START;
+ if(Crc) FilterParams.flags|=DMX_CHECK_CRC;
+ FilterParams.pid=Pid;
+ if(ioctl(fd,DMX_SET_FILTER,&FilterParams)<0) {
+ PRINTF(L_GEN_ERROR,"filter '%s': DMX_SET_FILTER failed: %s",id,strerror(errno));
+ return;
+ }
+ pid=Pid;
+ active=true;
+
+ LBSTART(L_CORE_ACTION);
+ LBPUT("filter '%s' -> pid=0x%04x sct=0x%02x/0x%02x/0x%02x crc=%d matching",id,Pid,Section,Mask,Mode,Crc);
+ int mam =Mask & (~Mode);
+ int manm=Mask & ~(~Mode);
+ for(int i=0; i<256; i++) {
+ int xxxor=Section ^ i;
+ if((mam&xxxor) || (manm && !(manm&xxxor))) {}
+ else
+ LBPUT(" 0x%02x",i);
+ }
+ LBEND();
+ }
+}
+
+void cPidFilter::Stop(void)
+{
+ cMutexLock lock(this);
+ if(fd>=0) {
+ CHECK(ioctl(fd,DMX_STOP));
+ active=false;
+ }
+}
+
+void cPidFilter::SetBuffSize(int BuffSize)
+{
+ cMutexLock lock(this);
+ if(fd>=0) {
+ Stop();
+ int s=max(BuffSize,8192);
+ CHECK(ioctl(fd,DMX_SET_BUFFER_SIZE,s));
+ }
+}
+
+int cPidFilter::SetIdleTime(unsigned int IdleTime)
+{
+ int i=idleTime;
+ idleTime=IdleTime;
+ return i;
+}
+
+void cPidFilter::Wakeup(void)
+{
+ cMutexLock lock(this);
+ forceRun=true;
+ PRINTF(L_CORE_ACTION,"filter '%s': wakeup",id);
+}
+
+int cPidFilter::Pid(void)
+{
+ return active ? pid : -1;
+}
+
+// -- cAction ------------------------------------------------------------------
+
+cAction::cAction(const char *Id, int DvbNum)
+{
+ asprintf(&id,"%s %d",Id,DvbNum);
+ dvbNum=DvbNum;
+ unique=0; pri=-1;
+ SetDescription("%s filter",id);
+}
+
+cAction::~cAction()
+{
+ Cancel(2);
+ DelAllFilter();
+ PRINTF(L_CORE_ACTION,"%s: stopped",id);
+ free(id);
+}
+
+cPidFilter *cAction::CreateFilter(const char *Id, int Num, int DvbNum, int IdleTime)
+{
+ return new cPidFilter(Id,Num,DvbNum,IdleTime);
+}
+
+cPidFilter *cAction::NewFilter(int IdleTime)
+{
+ Lock();
+ cPidFilter *filter=CreateFilter(id,unique++,dvbNum,IdleTime);
+ if(filter && filter->Open()) {
+ if(!Active()) {
+ Start();
+ PRINTF(L_CORE_ACTION,"%s: started",id);
+ }
+ filters.Add(filter);
+ }
+ else {
+ PRINTF(L_CORE_ACTION,"%s: filter '%s' failed to open",id,filter?filter->id:"XxX");
+ delete filter;
+ filter=0;
+ }
+ Unlock();
+ return filter;
+}
+
+cPidFilter *cAction::IdleFilter(void)
+{
+ Lock();
+ cPidFilter *filter;
+ for(filter=filters.First(); filter; filter=filters.Next(filter))
+ if(!filter->Active()) break;
+ Unlock();
+ return filter;
+}
+
+void cAction::DelFilter(cPidFilter *filter)
+{
+ Lock();
+ filters.Del(filter);
+ Unlock();
+}
+
+void cAction::DelAllFilter(void)
+{
+ Lock();
+ filters.Clear();
+ unique=0;
+ Unlock();
+}
+
+void cAction::Priority(int Pri)
+{
+ pri=Pri;
+}
+
+void cAction::Action(void)
+{
+ if(pri>0) SetPriority(pri);
+ while(Running()) {
+ int num=filters.Count();
+ if(num<=0) {
+ cCondWait::SleepMs(100);
+ }
+ else {
+ // first build pfd data
+ Lock();
+ cPidFilter *filter;
+ struct pollfd pfd[num];
+ num=0;
+ memset(pfd,0,sizeof(pfd));
+ for(filter=filters.First(); filter; filter=filters.Next(filter)) {
+ pfd[num].fd=filter->fd;
+ pfd[num].events=POLLIN;
+ num++;
+ }
+ Unlock();
+
+ // now poll for data
+ int r=poll(pfd,num,60);
+ if(r<0) {
+ PRINTF(L_GEN_ERROR,"action %s poll: %s",id,strerror(errno));
+ break;
+ }
+ if(r>0) {
+ for(r=0 ; r<num ; r++)
+ if(pfd[r].revents&POLLIN) {
+ Lock();
+ for(filter=filters.First(); filter; filter=filters.Next(filter)) {
+ if(filter->fd==pfd[r].fd) {
+ unsigned char buff[MAX_SECT_SIZE];
+ int n=read(filter->fd,buff,sizeof(buff));
+ if(n<0 && errno!=EAGAIN) {
+ if(errno==EOVERFLOW) PRINTF(L_GEN_ERROR,"action %s read: Buffer overflow",filter->id);
+ else PRINTF(L_GEN_ERROR,"action %s read: %s",filter->id,strerror(errno));
+ }
+ if(n>0) {
+ filter->lastTime.Set(); filter->forceRun=false;
+ Process(filter,buff,n);
+ // don't make any assumption about data-structs here
+ // Process() may have changed them
+ }
+ break;
+ }
+ }
+ Unlock();
+ }
+ }
+
+ // call filters which are idle too long
+ Lock();
+ do {
+ r=0;
+ for(filter=filters.First(); filter; filter=filters.Next(filter)) {
+ if(filter->forceRun || (filter->idleTime && filter->lastTime.Elapsed()>filter->idleTime)) {
+ filter->lastTime.Set(); filter->forceRun=false;
+ Process(filter,0,0);
+ // don't make any assumption about data-structs here
+ // Process() may have changed them
+ r=1; break;
+ }
+ }
+ } while(r);
+ Unlock();
+ }
+ }
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___FILTER_H
+#define ___FILTER_H
+
+#include <vdr/thread.h>
+#include "misc.h"
+
+class cPidFilter;
+
+// ----------------------------------------------------------------
+
+#define MAX_SECT_SIZE 4096
+
+class cAction : protected cThread {
+private:
+ char *id;
+ int dvbNum, unique, pri;
+ cSimpleList<cPidFilter> filters;
+protected:
+ virtual void Process(cPidFilter *filter, unsigned char *data, int len)=0;
+ virtual void Action(void);
+ virtual cPidFilter *CreateFilter(const char *Id, int Num, int DvbNum, int IdleTime);
+ //
+ cPidFilter *NewFilter(int IdleTime);
+ cPidFilter *IdleFilter(void);
+ void DelFilter(cPidFilter *filter);
+ void DelAllFilter(void);
+ void Priority(int Pri);
+public:
+ cAction(const char *Id, int DvbNum);
+ virtual ~cAction();
+ };
+
+// ----------------------------------------------------------------
+
+class cPidFilter : public cSimpleItem, private cMutex {
+friend class cAction;
+private:
+ int dvbNum;
+ unsigned int idleTime;
+ cTimeMs lastTime;
+ bool forceRun;
+ //
+ bool Open(void);
+protected:
+ char *id;
+ int fd;
+ int pid;
+ bool active;
+public:
+ void *userData;
+ //
+ cPidFilter(const char *Id, int Num, int DvbNum, unsigned int IdleTime);
+ virtual ~cPidFilter();
+ void Flush(void);
+ virtual void Start(int Pid, int Section, int Mask, int Mode, bool Crc);
+ void Stop(void);
+ void Wakeup(void);
+ void SetBuffSize(int BuffSize);
+ int SetIdleTime(unsigned int IdleTime);
+ int Pid(void);
+ bool Active(void) { return active; }
+ };
+
+#endif //___FILTER_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___HELPER_H
+#define ___HELPER_H
+
+#include <byteswap.h>
+
+#if defined __i386__
+#define get_misalign(_a) *(_a)
+#define put_misalign(_b,_a) *(_a)=(_b)
+#else
+template<class T> inline T get_misalign(T *p)
+{ struct s { T v; } __attribute__((packed)); return ((s *)p)->v; }
+
+template<class T> inline void put_misalign(unsigned int v, T* p)
+{ struct s { T v; } __attribute__((packed)); ((s *)p)->v = v; }
+#endif
+
+#define UINT32_BE(a) bswap_32(get_misalign((unsigned int *)(a)))
+#define UINT16_BE(a) bswap_16(get_misalign((unsigned short *)(a)))
+#define UINT8_BE(a) *((unsigned char *)a)
+#define BYTE4_BE(a,b) put_misalign(bswap_32(b),(unsigned int *)(a))
+#define BYTE2_BE(a,b) put_misalign(bswap_16(b),(unsigned short *)(a))
+#define BYTE1_BE(a,b) *(unsigned char *)(a)=(b)
+
+#define UINT32_LE(a) get_misalign((unsigned int *)(a))
+#define UINT16_LE(a) get_misalign((unsigned short *)(a))
+#define UINT8_LE(a) *((unsigned char *)a)
+#define BYTE4_LE(a,b) put_misalign(b,(unsigned int *)(a))
+#define BYTE2_LE(a,b) put_misalign(b,(unsigned short *)(a))
+#define BYTE1_LE(a,b) *(unsigned char *)(a)=(b)
+
+#define __ror16_const(x,xx) ((((x)&0xFFFF)>>(xx)) | ((x)<<(16-(xx))))
+#define __rol16_const(x,xx) (((x)<<(xx)) | (((x)&0xFFFF)>>(16-(xx))))
+#define __sn8_const(b) ((((b)>>4)&0xf) + (((b)&0xf)<<4))
+
+#if defined __GNUC__ && __GNUC__ >= 2 && defined __i386__
+
+#define ror16(x,xx) ({ unsigned int v; \
+ __asm__ ("rorw %2, %w0" : "=g" (v) : "0" (x), "i" (xx) : "cc"); \
+ v; })
+#define rol16(x,xx) ({ unsigned int v; \
+ __asm__ ("rolw %2, %w0" : "=g" (v) : "0" (x), "i" (xx) : "cc"); \
+ v; })
+#define sn8(b) ({ unsigned char v; \
+ if(__builtin_constant_p(b)) \
+ v=__sn8_const(b); \
+ else \
+ __asm__ ("rorb $4, %b0" : "=g" (v) : "0" (b) : "cc"); \
+ v; })
+
+#else //__GNUC__
+
+#define ror16(x,xx) __ror16_const(x,xx)
+#define rol16(x,xx) __rol16_const(x,xx)
+#define sn8(b) __sn8_const(b)
+
+#endif //__GNUC__
+
+static inline void __xxor(unsigned char *data, int len, const unsigned char *v1, const unsigned char *v2)
+{
+ switch(len) { // looks ugly, but the compiler can optimize it very well ;)
+ case 16:
+ *((unsigned int *)data+3) = *((unsigned int *)v1+3) ^ *((unsigned int *)v2+3);
+ *((unsigned int *)data+2) = *((unsigned int *)v1+2) ^ *((unsigned int *)v2+2);
+ case 8:
+ *((unsigned int *)data+1) = *((unsigned int *)v1+1) ^ *((unsigned int *)v2+1);
+ case 4:
+ *((unsigned int *)data+0) = *((unsigned int *)v1+0) ^ *((unsigned int *)v2+0);
+ break;
+ default:
+ while(len--) *data++ = *v1++ ^ *v2++;
+ break;
+ }
+}
+#define xxor(d,l,v1,v2) __xxor(d,l,v1,v2)
+
+#endif // ___HELPER_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include "i18n.h"
+
+const tI18nPhrase ScPhrases[] = {
+ { "Update keys (AU)",
+ "Keys updaten (AU)",
+ "",
+ "",
+ "Keys updaten (AU)",
+ "",
+ "Mise à jour des Clés (AU)",
+ "",
+ "Tilausten päivitys (AU)",
+ "Aktualizuj klucze (AU)",
+ "",
+ "",
+ "Nyckeluppdatering (AU)",
+ "",
+ "",
+ "",
+ "¾ÑÝÞÒÛïâì ÚÛîçØ (AU)",
+ },
+ { "active CAIDs",
+ "aktive CAIDs",
+ "",
+ "",
+ "actieve CAIDS",
+ "",
+ "CAIDs actifs",
+ "",
+ "aktiiviset CAID:t",
+ "aktywne CAID",
+ "",
+ "",
+ "aktiva CAID",
+ "",
+ "",
+ "",
+ "ÐÚâØÒÝëÕ CAID",
+ },
+ { "all CAIDs",
+ "alle CAIDs",
+ "",
+ "",
+ "alle CAIDs",
+ "",
+ "tous les CAIDs",
+ "",
+ "kaikki CAID:t",
+ "wszystkie CAID",
+ "",
+ "",
+ "alla CAID",
+ "",
+ "",
+ "",
+ "ÒáÕ CAID",
+ },
+ { "Active on DVB card",
+ "Aktiv auf DVB Karte",
+ "",
+ "",
+ "Actief op DVB kaart",
+ "",
+ "Actif sur la carte DVB",
+ "",
+ "Aktiivinen DVB-kortilla",
+ "Aktywny na karcie DVB",
+ "",
+ "",
+ "Aktiv på DVB-kort",
+ "",
+ "",
+ "",
+ "°ÚâØÒÝëÙ ÝÐ ßÛÐâÕ DVB",
+ },
+ { "Concurrent FF streams",
+ "Gleichzeitige FF Streams",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Yhtäaikainen salauksenpurku (FF)",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "¿ÐàÐÛÛÕÛìÝëÕ ßÞâÞÚØ FF",
+ },
+ { "Prefer local systems",
+ "Lokale Systeme bevorzugen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Suosi paikallista salauksenpurkua",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "¿àÕÔßÞçØâÐâì ÛÞÚÐÛìÝëÕ áØáâÕÜë",
+ },
+ { "Ignore CAID",
+ "Ignoriere CAID",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Jätä huomioimatta CAID",
+ "",
+ "",
+ "",
+ "Ignorera CAID",
+ "",
+ "",
+ "",
+ "¸ÓÝÞàØàÞÒÐâì CAID",
+ },
+ { "Key update status:",
+ "Key update Status:",
+ "",
+ "",
+ "Key update status:",
+ "",
+ "Mise à jour des clés Statut:",
+ "",
+ "Tilausten päivitys:",
+ "Stan aktualizacji kluczy:",
+ "",
+ "",
+ "Nyckeluppdateringsstatus:",
+ "",
+ "",
+ "",
+ "¾ÑÝÞÒÛÕÝØÕ ÚÛîçÕÙ:",
+ },
+ { " [Seen keys]", // 2 leading spaces
+ " [Gefundene Keys]",
+ "",
+ " [Chiavi trovate]",
+ "",
+ " [Gevonden keys]",
+ " [Clés trouvées]",
+ "",
+ " [Löydetyt päivitykset]",
+ " [widzianych kluczy]",
+ "",
+ " [vriskomena klidia]",
+ " [Påträffade nycklar]",
+ "",
+ "",
+ "",
+ " [ÚÛîçÕÙ ÝÐÙÔÕÝÞ]",
+ },
+ { " [New keys]", // 2 leading spaces
+ " [Neue Keys]",
+ "",
+ " [Nuove chiavi]",
+ " [Nieuwe keys]",
+ "",
+ " [Nouvelles clés]",
+ "",
+ " [Uudet päivitykset]",
+ " [nowych kluczy]",
+ "",
+ " [Nea klidia]",
+ " [Nya nycklar]",
+ "",
+ "",
+ "",
+ " [ÝÞÒëå ÚÛîçÕÙ]",
+ },
+ { "Current keys:",
+ "Aktuelle Keys:",
+ "",
+ "Chiave in uso:",
+ "Huidige keys:",
+ "",
+ "Clé courante:",
+ "",
+ "Nykyiset avaimet:",
+ "Obecne klucze:",
+ "",
+ "Energo klidi kartas:",
+ "Aktuell nyckel:",
+ "",
+ "",
+ "",
+ "¸áßÞÛì×ãÕÜëÕ ÚÛîçØ:",
+ },
+ { "(none)",
+ "(keiner)",
+ "",
+ "(nessuno)",
+ "(geen)",
+ "",
+ "(aucune)",
+ "",
+ "(ei)",
+ "(brak)",
+ "",
+ "(kanena)",
+ "(ingen)",
+ "",
+ "",
+ "",
+ "(ÝÕâ)",
+ },
+ { "undisclosed key",
+ "unbekannter Key",
+ "",
+ "",
+ "onbekende key",
+ "",
+ "Clé non-révélée",
+ "",
+ "tuntematon avain",
+ "niejawny klucz",
+ "",
+ "",
+ "ej avslöjad nyckel",
+ "",
+ "",
+ "",
+ "ÝÕØ×ÒÕáâÝëÙ ÚÛîç",
+ },
+ { "Status information...",
+ "Status Informationen...",
+ "",
+ "",
+ "Status informatie...",
+ "",
+ "Statut information...",
+ "",
+ "Tilannetiedot...",
+ "Informacje o stanie...",
+ "",
+ "",
+ "Statusinformation...",
+ "",
+ "",
+ "",
+ "ÁÞáâÞïÝØÕ...",
+ },
+ { "Flush ECM cache",
+ "ECM Zwischenspeicher leeren",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Tyhjennä ECM-välimuisti",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "¾çØáâØâì Úíè ECM",
+ },
+ { "Really flush ECM cache?",
+ "ECM Zwischenspeicher wirklich leeren?",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Tyhjennetäänkö ECM-välimuisti?",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "´ÕÙáâÒØâÕÛìÝÞ ÞçØáâØâì Úíè ECM?",
+ },
+ { "Reload files",
+ "Dateien neu laden",
+ "",
+ "",
+ "Opnieuw laden bestanden?",
+ "",
+ "Recharger les fichiers",
+ "",
+ "Lataa avaintiedostot uudelleen",
+ "Prze³aduj pliki",
+ "",
+ "Ksanadiavasma arxeion",
+ "Ladda om filer",
+ "",
+ "",
+ "",
+ "¿ÕàÕ×ÐÓàãרâì äÐÙÛë",
+ },
+ { "Really reload files?",
+ "Dateien wirklich neu laden?",
+ "",
+ "Ricarica il file delle chiavi?",
+ "Werkelijk opnieuw laden bestanden?",
+ "",
+ "Vraiment recharger les fichiers?",
+ "",
+ "Ladataanko avaintiedostot uudelleen?",
+ "Na pewno prze³adowaæ klucze?",
+ "",
+ "Na diavastoun pali ta arxeia?",
+ "Bekräfta omladdning av filer?",
+ "",
+ "",
+ "",
+ "´ÕÙáâÒØâÕÛìÝÞ ßÕàÕ×ÐÓàãרâì äÐÙÛë?",
+ },
+ { "Active! Can't reload files now",
+ "Aktiv! Kann Dateien jetzt nicht neu laden",
+ "",
+ "Chiave in uso!",
+ "Actief! Kan bestanden niet herladen",
+ "",
+ "Actif ! Je ne peux pas recharger maintenant",
+ "",
+ "Aktiivinen! Uudelleen lataus ei onnistu",
+ "Aktywny! Nie mo¿na teraz prze³adowaæ plików.",
+ "",
+ "Energo! Den ine dinato to ksanadiavasma",
+ "Aktiv! Kan inte ladda om filer nu",
+ "",
+ "",
+ "",
+ "·ÐÝïâ! ½Õ ÜÞÓã ßÕàÕ×ÐÓàãרâì äÐÙÛë",
+ },
+ { "Smartcard",
+ "Smartcard",
+ "",
+ "",
+ "Smartcard",
+ "",
+ "Smartcard",
+ "",
+ "Älykortti",
+ "Smartcard",
+ "",
+ "",
+ "Smartcard",
+ "",
+ "",
+ "",
+ "ºÐàâÞçÚÐ",
+ },
+ { "Reset card",
+ "Karte reseten",
+ "",
+ "",
+ "Reset smartcard",
+ "",
+ "Réinitialiser la carte",
+ "",
+ "Nollaa kortti",
+ "Resetuj kartê",
+ "",
+ "",
+ "Nollställ kortet",
+ "",
+ "",
+ "",
+ "ÁÑàÞáØâì ÚÐàâÞçÚã",
+ },
+ { "Really reset card?",
+ "Karte wirklich reseten?",
+ "",
+ "",
+ "Werkelijk smartcard resetten?",
+ "",
+ "Réinitialiser vraiment la carte?",
+ "",
+ "Nollataanko kortti?",
+ "Na pewno zrestartowaæ kartê?",
+ "",
+ "",
+ "Bekräfta nollställning av kortet?",
+ "",
+ "",
+ "",
+ "´ÕÙáâÒØâÕÛìÝÞ áÑàÞáØâì ÚÐàâÞçÚã?",
+ },
+ { "Smartcard interface",
+ "Smartcard Interface",
+ "",
+ "",
+ "Smartcard interface",
+ "",
+ "Interface Smartcard",
+ "",
+ "Älykorttiliitäntä",
+ "Interfejs Smartcard",
+ "",
+ "",
+ "Smartcard-gränssnitt",
+ "",
+ "",
+ "",
+ "ºÐàâÞçÝëÙ ÜÞÔãÛì",
+ },
+ { "(empty)",
+ "(leer)",
+ "",
+ "(nessuna)",
+ "(leeg)",
+ "",
+ "(vide)",
+ "",
+ "(tyhjä)",
+ "(puste)",
+ "",
+ "",
+ "(tom)",
+ "",
+ "",
+ "",
+ "(ßãáâÞ)",
+ },
+ { "SoftCAM",
+ "SoftCAM",
+ "",
+ "SoftCAM",
+ "SoftCAM",
+ "",
+ "SoftCAM",
+ "",
+ "SoftCAM",
+ "SoftCAM",
+ "",
+ "SoftCAM",
+ "SoftCAM",
+ "",
+ "",
+ "",
+ "SoftCAM",
+ },
+ { "A software emulated CAM",
+ "Ein Software emuliertes CAM",
+ "",
+ "",
+ "In software geëmuleerde CAM",
+ "",
+ "Un logiciel emulateur de CAM",
+ "",
+ "Ohjelmistopohjainen salauksenpurku",
+ "Programowo emulowany CAM",
+ "",
+ "",
+ "En mjukvaruemulerad CAM",
+ "",
+ "",
+ "",
+ "¿àÞÓàÐÜÝëÙ íÜãÛïâÞà CAM",
+ },
+ { "Cryptsystem options...",
+ "Cryptsystem Optionen...",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Salausjärjestelmien asetukset...",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "¾ßæØØ ÚàØßâÞáØáâÕÜ...",
+ },
+ { "Cryptsystem options",
+ "Cryptsystem Optionen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Salausjärjestelmien asetukset",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "¾ßæØØ ÚàØßâÞáØáâÕÜ",
+ },
+ { "Message logging...",
+ "Meldungsprotokolierung...",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Viestien tulostus...",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "¿àÞâÞÚÞÛØàÞÒÐÝØÕ áÞÞÑéÕÝØÙ...",
+ },
+ { "Message logging",
+ "Meldungsprotokolierung",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Viestien tulostus",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "¿àÞâÞÚÞÛØàÞÒÐÝØÕ áÞÞÑéÕÝØÙ",
+ },
+ { "Log to console",
+ "Meldungen auf Konsole",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Tulosta konsoliin",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "ÁÞÞÑéÕÝØï Ò ÚÞÝáÞÛÕ",
+ },
+ { "Log to file",
+ "Meldungen in Datei",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Tulosta tiedostoon",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "ÁÞÞÑéÕÝØï Ò äÐÙÛ",
+ },
+ { "Filename",
+ "Dateiname",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Tiedoston nimi",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "½Ð×ÒÐÝØÕ äÐÙÛÐ",
+ },
+ { "Log to syslog",
+ "Meldungen in Syslog",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Tulosta systeemilokiin",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "ÁÞÞÑéÕÝØï Ò Syslog",
+ },
+ { "Disable ALL modules",
+ "ALLE Module ausschalten",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Poista kaikki moduulit käytöstä",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "²ëÚÛîçØâì ÒáÕ ÜÞÔãÛØ",
+ },
+ { "Really disable ALL modules?",
+ "Wirklich ALLE Module ausschalten?",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Poistetaanko kaikki moduulit käytöstä?",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "´ÕÙáâÒØâÕÛìÝÞ ÒëÚÛîçØâì ÒáÕ ÜÞÔãÛØ?",
+ },
+ { "Reset ALL modules to default",
+ "ALLE Module auf Standard zurücksetzen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Nollaa kaikki moduulit oletusarvoihin",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "ÁÑàÞáØâì ÒáÕ ÜÞÔãÛØ ßÞ ãÜÞÛçÐÝØî",
+ },
+ { "Really reset ALL modules to default?",
+ "Wirklich ALLE Module auf Standard zurücksetzen?",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Nollataanko kaikki moduulit oletusarvoihin?",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "´ÕÙáâÒØâÕÛìÝÞ áÑàÞáØâì ÒáÕ ÜÞÔãÛØ ßÞ ãÜÞÛçÐÝØî?",
+ },
+ { "Module",
+ "Modul",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Moduuli",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "¼ÞÔãÛì",
+ },
+ { "Module config",
+ "Modul Einstellungen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Moduulin asetukset",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "½ÐáâàÞÙÚÐ ÜÞÔãÛï",
+ },
+ { "Reset module to default",
+ "Modul auf Standard zurücksetzen",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Nollaa moduuli oletusarvoihin",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "ÁÑàÞá ÜÞÔãÛï ßÞ ãÜÞÛçÐÝØî",
+ },
+ { "Really reset module to default?",
+ "Wirklich Modul auf Standard zurücksetzen?",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Nollataanko moduuli oletusarvoihin?",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "´ÕÙáâÒØâÕÛìÝÞ áÑàÞáØâì ÜÞÔãÛì ßÞ ãÜÞÛçÐÝØî?",
+ },
+ { NULL }
+ };
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___I18N_H
+#define ___I18N_H
+
+#include <vdr/i18n.h>
+
+extern const tI18nPhrase ScPhrases[];
+
+#endif //___I18N_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___LOG_CORE_H
+#define ___LOG_CORE_H
+
+#include "log.h"
+
+// ----------------------------------------------------------------
+
+#define L_CORE 1
+
+#define L_CORE_LOAD LCLASS(L_CORE,0x2)
+#define L_CORE_ACTION LCLASS(L_CORE,0x4)
+#define L_CORE_ECM LCLASS(L_CORE,0x8)
+#define L_CORE_ECMPROC LCLASS(L_CORE,0x10)
+#define L_CORE_PIDS LCLASS(L_CORE,0x20)
+#define L_CORE_AU LCLASS(L_CORE,0x40)
+#define L_CORE_AUSTATS LCLASS(L_CORE,0x80)
+#define L_CORE_AUEXTRA LCLASS(L_CORE,0x100)
+#define L_CORE_AUEXTERN LCLASS(L_CORE,0x200)
+#define L_CORE_CAIDS LCLASS(L_CORE,0x400)
+#define L_CORE_KEYS LCLASS(L_CORE,0x800)
+#define L_CORE_DYN LCLASS(L_CORE,0x1000)
+#define L_CORE_CSA LCLASS(L_CORE,0x2000)
+#define L_CORE_CI LCLASS(L_CORE,0x4000)
+#define L_CORE_AV7110 LCLASS(L_CORE,0x8000)
+#define L_CORE_NET LCLASS(L_CORE,0x10000)
+#define L_CORE_NETDATA LCLASS(L_CORE,0x20000)
+#define L_CORE_MSGCACHE LCLASS(L_CORE,0x40000)
+#define L_CORE_SERIAL LCLASS(L_CORE,0x80000)
+#define L_CORE_SC LCLASS(L_CORE,0x100000)
+#define L_CORE_HOOK LCLASS(L_CORE,0x200000)
+#define L_CORE_CIFULL LCLASS(L_CORE,0x400000)
+
+#define L_CORE_ALL LALL(L_CORE_CIFULL)
+
+#endif //___LOG_CORE_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___LOG_SC_H
+#define ___LOG_SC_H
+
+#include "log.h"
+
+#define L_SC_ERROR LCLASS(L_SC,0x2)
+#define L_SC_INIT LCLASS(L_SC,0x4)
+
+#define L_SC_LASTDEF L_SC_INIT
+#define L_SC_DEFDEF L_SC_ERROR|L_SC_INIT
+#define L_SC_DEFNAMES "error","init"
+
+#endif //___LOG_SC_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___LOG_SYS_H
+#define ___LOG_SYS_H
+
+#include "log.h"
+
+#define L_SYS_KEY LCLASS(L_SYS,0x2)
+#define L_SYS_ECM LCLASS(L_SYS,0x4)
+#define L_SYS_EMM LCLASS(L_SYS,0x8)
+#define L_SYS_CRYPTO LCLASS(L_SYS,0x10)
+#define L_SYS_VERBOSE LCLASS(L_SYS,0x20)
+
+#define L_SYS_LASTDEF L_SYS_VERBOSE
+#define L_SYS_DEFDEF L_SYS_KEY|L_SYS_ECM
+#define L_SYS_DEFNAMES "key","ecm","emm","crypto","verbose"
+
+#endif //___LOG_SYS_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+#include "log.h"
+#include "misc.h"
+
+#define LMOD_SUP 32
+#define LMOD_CFG_VALID 0x80000000
+
+struct LogHeader {
+ int c;
+ char stamp[32];
+ char tag[64];
+ };
+
+struct LogConfig logcfg = {
+ 1,0,0,
+ "/var/log/vdr-sc"
+ };
+
+static const struct LogModule lm_general = {
+ (LMOD_ENABLE|L_GEN_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_GEN_ALL)&LOPT_MASK,
+ "general", { "error","warn","info","debug","misc" }
+ };
+
+static const struct LogModule *mods[LMOD_SUP] = { &lm_general };
+static int config[LMOD_SUP] = { LMOD_ENABLE|L_GEN_ALL|LMOD_CFG_VALID };
+static int cmask[LMOD_SUP] = { LMOD_ENABLE|L_GEN_ALL };
+static cMutex logfileMutex;
+static FILE *logfile=0;
+static bool logfileShutup=false, logfileReopen=false;
+
+static unsigned int lastCrc=0;
+static int lastC=0, lastCount=0;
+static cTimeMs lastTime;
+static cMutex lastMutex;
+
+// -- cLogging -----------------------------------------------------------------
+
+bool cLogging::AddModule(int m, const struct LogModule *lm)
+{
+ if(m<LMOD_SUP) {
+ if(mods[m]) Printf(L_GEN_DEBUG,"duplicate logging module id %d (%s & %s)",m,mods[m]->Name,lm->Name);
+ mods[m]=lm;
+ if(config[m]&LMOD_CFG_VALID) UpgradeOptions(m);
+ else SetModuleDefault(LCLASS(m,0));
+//printf("module %-16s added. mod=%x supp=%x def=%x cfg=%x\n",lm->Name,m,lm->OptSupported,lm->OptDefault,config[m]);
+ }
+ else Printf(L_GEN_DEBUG,"failed to add logging module %d (%s)",m,lm->Name);
+ return true;
+}
+
+const struct LogModule *cLogging::GetModule(int c)
+{
+ int m=LMOD(c);
+ return m<LMOD_SUP ? mods[m] : 0;
+}
+
+bool cLogging::GetHeader(int c, struct LogHeader *lh)
+{
+ const struct LogModule *lm=GetModule(c);
+ if(lm) {
+ time_t tt=time(0);
+ struct tm tm_r;
+ strftime(lh->stamp,sizeof(lh->stamp),"%b %e %T",localtime_r(&tt,&tm_r));
+ int i, o=LOPT(c)&~LMOD_ENABLE;
+ for(i=0; i<LOPT_NUM; i++,o>>=1) if(o&1) break;
+ snprintf(lh->tag,sizeof(lh->tag),"%s.%s",lm->Name,(i>=1 && i<LOPT_NUM && lm->OptName[i-1])?lm->OptName[i-1]:"unknown");
+ lh->c=c;
+ return true;
+ }
+ return false;
+}
+
+void cLogging::LogLine(const struct LogHeader *lh, const char *txt, bool doCrc)
+{
+ if(doCrc) {
+ unsigned int crc=crc32_le(0,(const unsigned char *)txt,strlen(txt)+1);
+ lastMutex.Lock();
+ if(lastCrc==crc && lastC==lh->c && ++lastCount<4000000 && lastTime.Elapsed()<=30*1000) {
+ lastMutex.Unlock();
+ return;
+ }
+ else {
+ unsigned int saveCrc=lastCrc;
+ int saveCount=lastCount, saveC=lastC;
+ lastTime.Set();
+ lastCrc=crc; lastC=lh->c; lastCount=0;
+ lastMutex.Unlock();
+ if(saveCount>1) {
+ struct LogHeader lh2;
+ if(GetHeader(saveC,&lh2)) {
+ char buff[128];
+ snprintf(buff,sizeof(buff),"last message repeated %d times",saveCount);
+ LogLine(&lh2,buff,false);
+ }
+ if(saveCrc==crc && saveC==lh->c) return;
+ }
+ }
+ }
+
+ if(logcfg.logCon)
+ printf("%s [%s] %s\n",lh->stamp,lh->tag,txt);
+
+ if(logcfg.logFile) {
+ if((!logfile && !logfileShutup) || logfileReopen) {
+ logfileMutex.Lock();
+ if(logfileReopen) {
+ logfileReopen=false;
+ if(logfile) {
+ PRINTF(L_GEN_DEBUG,"logfile closed, reopen as '%s'",logcfg.logFilename);
+ fclose(logfile);
+ logfile=0;
+ }
+ }
+ if(!logfile) {
+ logfile=fopen(logcfg.logFilename,"a");
+ if(logfile) {
+ setlinebuf(logfile);
+ PRINTF(L_GEN_DEBUG,"logfile '%s' opened",logcfg.logFilename);
+ }
+ else {
+ logfileShutup=true;
+ PRINTF(L_GEN_ERROR,"failed to open logfile '%s': %s",logcfg.logFilename,strerror(errno));
+ }
+ }
+ logfileMutex.Unlock();
+ }
+ if(logfile)
+ fprintf(logfile,"%s [%s] %s\n",lh->stamp,lh->tag,txt);
+ }
+
+ if(logcfg.logSys || LMOD(lh->c)==L_GEN) {
+ int pri=-1;
+ switch(lh->c) {
+ case L_GEN_ERROR:
+ case L_GEN_WARN: if(SysLogLevel>0) pri=LOG_ERR; break;
+ case L_GEN_INFO: if(SysLogLevel>1) pri=LOG_INFO; break;
+ case L_GEN_DEBUG: if(SysLogLevel>2) pri=LOG_DEBUG; break;
+ case L_GEN_MISC: if(logcfg.logSys) pri=LOG_DEBUG; break;
+ default: pri=LOG_DEBUG; break;
+ }
+ if(pri>=0)
+ syslog(pri,"[%d] [%s] %s",cThread::ThreadId(),lh->tag,txt);
+ }
+}
+
+bool cLogging::Enabled(int c)
+{
+ int m=LMOD(c);
+ int o=LOPT(c)|LMOD_CFG_VALID|LMOD_ENABLE;
+ return m<LMOD_SUP && (config[m] & o)==o;
+}
+
+void cLogging::Printf(int c, const char *format, ...)
+{
+ if(Enabled(c)) {
+ struct LogHeader lh;
+ if(GetHeader(c,&lh)) {
+ char buff[1024];
+ va_list ap;
+ va_start(ap,format);
+ vsnprintf(buff,sizeof(buff),format,ap);
+ va_end(ap);
+ LogLine(&lh,buff);
+ }
+ }
+}
+
+void cLogging::Puts(int c, const char *txt)
+{
+ if(txt && Enabled(c)) {
+ struct LogHeader lh;
+ if(GetHeader(c,&lh)) {
+ LogLine(&lh,txt);
+ }
+ }
+}
+
+void cLogging::PutLB(int c, cLineBuff *lb)
+{
+ if(lb->Length()>0) {
+ Puts(c,lb->Line());
+ lb->Flush();
+ }
+}
+
+void cLogging::Dump(int c, const void *data, int n, const char *format, ...)
+{
+ if(Enabled(c)) {
+ struct LogHeader lh;
+ if(GetHeader(c,&lh)) {
+ char buff[1024];
+ va_list ap;
+ va_start(ap,format);
+ vsnprintf(buff,sizeof(buff),format,ap);
+ va_end(ap);
+ LogLine(&lh,buff);
+ const unsigned char *d=(const unsigned char *)data;
+ for(int i=0; i<n;) {
+ int q=sprintf(buff,"%04x:",i);
+ for(int l=0 ; l<16 && i<n ; l++) q+=sprintf(&buff[q]," %02x",d[i++]);
+ LogLine(&lh,buff);
+ }
+ }
+ }
+}
+
+void cLogging::LineDump(int c, const void *data, int n, const char *format, ...)
+{
+ if(Enabled(c)) {
+ struct LogHeader lh;
+ if(GetHeader(c,&lh)) {
+ char buff[4096];
+ va_list ap;
+ va_start(ap,format);
+ unsigned int q=vsnprintf(buff,sizeof(buff),format,ap);
+ va_end(ap);
+ const unsigned char *d=(const unsigned char *)data;
+ for(int i=0; i<n; i++) {
+ if(q>=sizeof(buff)-8) {
+ q+=snprintf(&buff[q],sizeof(buff)-q,"....");
+ break;
+ }
+ q+=snprintf(&buff[q],sizeof(buff)-q," %02x",d[i]);
+ }
+ LogLine(&lh,buff);
+ }
+ }
+}
+
+void cLogging::SetModuleDefault(int c)
+{
+ const struct LogModule *lm=GetModule(c);
+ if(lm) {
+ int m=LMOD(c);
+ config[m]=(lm->OptDefault&LOPT_MASK)|LMOD_CFG_VALID;
+ cmask[m] = lm->OptSupported&LOPT_MASK;
+ }
+}
+
+void cLogging::SetModuleOptions(int c)
+{
+ const struct LogModule *lm=GetModule(c);
+ if(lm) config[LMOD(c)]=(LOPT(c)&(lm->OptSupported&LOPT_MASK))|LMOD_CFG_VALID;
+}
+
+int cLogging::GetModuleOptions(int c)
+{
+ const struct LogModule *lm=GetModule(c);
+ return lm ? LOPT(config[LMOD(c)]) : -1;
+}
+
+void cLogging::SetModuleOption(int c, bool on)
+{
+ const struct LogModule *lm=GetModule(c);
+ if(lm) {
+ int m=LMOD(c);
+ int o=LOPT(c)&(lm->OptSupported&LOPT_MASK);
+ if(o) {
+ if(on) config[m]|=o; else config[m]&=(~o);
+ config[m]|=LMOD_CFG_VALID;
+ }
+ }
+}
+
+void cLogging::UpgradeOptions(int m)
+{
+ const struct LogModule *lm=mods[m];
+ if(lm) {
+ config[m]&=(lm->OptSupported&LOPT_MASK);
+ config[m]|=(lm->OptDefault&LOPT_MASK)&(~cmask[m]);
+ cmask[m] =lm->OptSupported&LOPT_MASK;
+ config[m]|=LMOD_CFG_VALID;
+ }
+}
+
+const char *cLogging::GetModuleName(int c)
+{
+ const struct LogModule *lm=GetModule(c);
+ return lm ? lm->Name : 0;
+}
+
+const char *cLogging::GetOptionName(int c)
+{
+ const struct LogModule *lm=GetModule(c);
+ if(lm) {
+ int o=LOPT(c)&~LMOD_ENABLE;
+ if(lm->OptSupported & o) {
+ int i;
+ for(i=0; i<LOPT_NUM; i++,o>>=1) if(o&1) break;
+ return (i>=1 && i<LOPT_NUM && lm->OptName[i-1]) ? lm->OptName[i-1] : 0;
+ }
+ }
+ return 0;
+}
+
+bool cLogging::GetConfig(cLineBuff *lb)
+{
+ lb->Flush();
+ bool cont=false;
+ for(int m=1; m<LMOD_SUP; m++)
+ if(config[m]&LMOD_CFG_VALID) {
+ lb->Printf("%s%d:%x:%x",cont?",":"",m,config[m]&~LMOD_CFG_VALID,cmask[m]);
+ cont=true;
+ }
+ return cont;
+}
+
+void cLogging::ParseConfig(const char *txt)
+{
+ int n=0, l, m, o, d;
+ while(sscanf(&txt[n],"%d:%x:%x%n",&m,&o,&d,&l)==3) {
+ if(m>=1 && m<LMOD_SUP) {
+ config[m]=(o&LOPT_MASK)|LMOD_CFG_VALID;
+ cmask[m] =(d&LOPT_MASK);
+ UpgradeOptions(m);
+ }
+ n+=l;
+ if(txt[n]!=',') break;
+ n++;
+ }
+}
+
+void cLogging::ReopenLogfile(void)
+{
+ logfileMutex.Lock();
+ logfileReopen=true;
+ logfileMutex.Unlock();
+}
+
+int cLogging::GetClassByName(const char *name)
+{
+ const char *s=index(name,'.');
+ if(s) {
+ unsigned int l=s-name;
+ for(int m=1; m<LMOD_SUP; m++) {
+ const struct LogModule *lm=mods[m];
+ if(lm && strlen(lm->Name)==l && !strncasecmp(name,lm->Name,l)) {
+ s++;
+ if(!strcasecmp(s,"enable"))
+ return LCLASS(m,1);
+ for(int o=1; o<LOPT_NUM; o++)
+ if((lm->OptSupported&(1<<o)) && lm->OptName[o-1] && !strcasecmp(s,lm->OptName[o-1]))
+ return LCLASS(m,1<<o);
+ break;
+ }
+ }
+ }
+ return -1;
+}
+
+// -- cLogLineBuff -------------------------------------------------------------
+
+cLogLineBuff::cLogLineBuff(int C)
+:cLineBuff(256)
+{
+ c=C;
+}
+
+cLogLineBuff::~cLogLineBuff()
+{
+ Flush();
+}
+
+void cLogLineBuff::Flush(void)
+{
+ cLogging::PutLB(c,this);
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___LOG_H
+#define ___LOG_H
+
+#include "misc.h"
+
+// ----------------------------------------------------------------
+
+#define LOPT_NUM 24
+#define LOPT_MAX (1<<LOPT_NUM)
+#define LOPT_MASK (LOPT_MAX-1)
+#define LMOD_MAX (1<<(32-LOPT_NUM))
+#define LMOD_MASK (LMOD_MAX-1)
+#define LMOD_ENABLE 1
+
+#define LCLASS(m,o) ((((m)&LMOD_MASK)<<LOPT_NUM)|((o)&LOPT_MASK))
+#define LMOD(c) (((c)>>LOPT_NUM)&LMOD_MASK)
+#define LOPT(o) ((o)&LOPT_MASK)
+#define LALL(o) (((o)<<1)-1)
+
+#define ADD_MODULE(MOD,NAME) static bool __add_mod_ ## NAME = cLogging::AddModule(MOD,&NAME);
+
+#define PRINTF(c,t...) cLogging::Printf((c),t)
+#define PUTS(c,t) cLogging::Puts((c),(t))
+#define PUTLB(c,lb) cLogging::PutLB((c),(lb))
+#define HEXDUMP(c,d,n,t...) cLogging::Dump((c),(d),(n),t)
+#define LDUMP(c,d,n,t...) cLogging::LineDump((c),(d),(n),t)
+#define LOG(c) cLogging::Enabled((c))
+
+// backward compatibility
+#define HexDump(d,n) do { int __n=(n); HEXDUMP(L_GEN_MISC,(d),__n,"dump: n=%d/0x%04x",__n,__n); } while(0)
+
+#define LBSTART(c) do { int __c=(c); if(LOG(__c)) { cLogLineBuff __llb(__c)
+#define LBSTARTF(c) do { int __c=(c); { cLogLineBuff __llb(__c)
+#define LBPUT(t...) __llb.Printf(t)
+#define LBFLUSH() __llb.Flush()
+#define LBEND( ) } } while(0)
+
+// ----------------------------------------------------------------
+
+#define L_GEN 0
+#define L_GEN_ERROR LCLASS(L_GEN,0x2)
+#define L_GEN_WARN LCLASS(L_GEN,0x4)
+#define L_GEN_INFO LCLASS(L_GEN,0x8)
+#define L_GEN_DEBUG LCLASS(L_GEN,0x10)
+#define L_GEN_MISC LCLASS(L_GEN,0x20)
+
+#define L_GEN_ALL LALL(L_GEN_MISC)
+
+// ----------------------------------------------------------------
+
+struct LogConfig {
+ int logCon, logFile, logSys;
+ char logFilename[128];
+ };
+
+extern struct LogConfig logcfg;
+
+struct LogModule {
+ int OptSupported, OptDefault;
+ const char *Name, *OptName[LOPT_NUM];
+ };
+
+struct LogHeader;
+
+class cLogging {
+private:
+ static bool GetHeader(int c, struct LogHeader *lh);
+ static void LogLine(const struct LogHeader *lh, const char *txt, bool doCrc=true);
+ static const struct LogModule *GetModule(int c);
+ static void UpgradeOptions(int m);
+public:
+ static bool AddModule(int m, const struct LogModule *dm);
+ //
+ static void Printf(int c, const char *format, ...) __attribute__ ((format (printf,2,3)));
+ static void Puts(int c, const char *txt);
+ static void PutLB(int c, cLineBuff *lb);
+ static void Dump(int c, const void *data, int n, const char *format, ...) __attribute__ ((format (printf,4,5)));
+ static void LineDump(int c, const void *data, int n, const char *format, ...) __attribute__ ((format (printf,4,5)));
+ static bool Enabled(int c);
+ //
+ static void SetModuleOptions(int c);
+ static int GetModuleOptions(int c);
+ static void SetModuleOption(int c, bool on);
+ static void SetModuleDefault(int c);
+ static const char *GetModuleName(int c);
+ static const char *GetOptionName(int c);
+ static void ParseConfig(const char *txt);
+ static bool GetConfig(cLineBuff *lb);
+ static void ReopenLogfile(void);
+ static int GetClassByName(const char *name);
+ };
+
+// ----------------------------------------------------------------
+
+class cLogLineBuff : public cLineBuff {
+private:
+ int c;
+public:
+ cLogLineBuff(int C);
+ ~cLogLineBuff();
+ void Flush(void);
+ };
+
+#endif //___LOG_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <fcntl.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+#include "misc.h"
+
+// -----------------------------------------------------------------------------
+
+void DvbName(const char *Name, int n, char *buffer, int len)
+{
+ snprintf(buffer,len,"/dev/dvb/adapter%d/%s%d",n,Name,0);
+}
+
+int DvbOpen(const char *Name, int n, int Mode, bool ReportError)
+{
+ char FileName[128];
+ DvbName(Name,n,FileName,sizeof(FileName));
+ int fd=open(FileName,Mode);
+ if(fd<0 && ReportError) LOG_ERROR_STR(FileName);
+ return fd;
+}
+
+const char *HexStr(char *str, const unsigned char *mem, int len)
+{
+ for(int i=0 ; i<len ; i++) sprintf(&str[i*2],"%02X",mem[i]);
+ return str;
+}
+
+static int HexDigit(int asc)
+{
+ if(asc>='A') return toupper(asc)-'A'+10;
+ return asc-'0';
+}
+
+int GetChar(const char * &line, int *store, int count)
+{
+ line=skipspace(line);
+ const char *sline=line;
+ *store=0;
+ while(count) {
+ if(line[0]==0 || !isalnum(line[0])) break;
+ *store<<=8; *store+=line[0];
+ line++; count--;
+ }
+ if(!count && (!*line || isspace(*line) || *line==';')) return sline-line;
+ *store=0;
+ line=sline;
+ return 0;
+}
+
+static int GetHexBase(const char * &line, unsigned char *store, int count, bool fixedLen, bool asc)
+{
+ line=skipspace(line);
+ const char *sline=line;
+ unsigned char *sstore=store;
+ int scount=count;
+ while(count) {
+ if(line[0]==0 || line[1]==0 || !isxdigit(line[0]) || !isxdigit(line[1])) break;
+ if(asc) {
+ *store++=line[0]; *store++=line[1];
+ }
+ else {
+ int d1=HexDigit(line[0]);
+ int d0=HexDigit(line[1]);
+ *store++=(d1<<4) + d0;
+ }
+ line+=2; count--;
+ }
+ if(asc) *store=0; // make sure strings are NULL terminated
+ if((!count || (!fixedLen && count!=scount)) &&
+ (!*line || isspace(*line) || *line==';' || *line==']' || *line=='/') ) return scount-count;
+ memset(sstore,0,scount);
+ line=sline;
+ return 0;
+}
+
+int GetHex(const char * &line, unsigned char *store, int count, bool fixedLen)
+{
+ return GetHexBase(line,store,count,fixedLen,false);
+}
+
+int GetHexAsc(const char * &line, unsigned char *store, int count)
+{
+ return GetHexBase(line,store,count/2,true,true)*2;
+}
+
+unsigned long long Bin2LongLong(unsigned char *mem, int len)
+{
+ unsigned long long k=0;
+ for(int i=0 ; i<len ; i++) { k<<=8; k+=mem[i]; }
+ return k;
+}
+
+bool CheckNull(const unsigned char *data, int len)
+{
+ while(--len>=0) if(data[len]) return false;
+ return true;
+}
+
+bool CheckFF(const unsigned char *data, int len)
+{
+ while(--len>=0) if(data[len]!=0xFF) return false;
+ return true;
+}
+
+unsigned char XorSum(const unsigned char *mem, int len)
+{
+ unsigned char cs=0;
+ while(len>0) { cs ^= *mem++; len--; }
+ return cs;
+}
+
+// crc stuff taken from linux-2.6.0/lib/crc32.c
+/*
+ * There are multiple 16-bit CRC polynomials in common use, but this is
+ * *the* standard CRC-32 polynomial, first popularized by Ethernet.
+ * x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x^1+x^0
+ */
+#define CRCPOLY_LE 0xedb88320
+
+unsigned int crc32_le(unsigned int crc, unsigned char const *p, int len)
+{
+ crc^=0xffffffff; // zlib mod
+ while(len--) {
+ crc^=*p++;
+ for(int i=0; i<8; i++)
+ crc=(crc&1) ? (crc>>1)^CRCPOLY_LE : (crc>>1);
+ }
+ crc^=0xffffffff; // zlib mod
+ return crc;
+}
+
+// -- cLineBuff -----------------------------------------------------------------
+
+cLineBuff::cLineBuff(int blocksize)
+{
+ blockSize=blocksize;
+ work=0;
+ blen=wlen=0;
+}
+
+cLineBuff::~cLineBuff()
+{
+ free(work);
+}
+
+void cLineBuff::Flush(void)
+{
+ blen=0;
+}
+
+char *cLineBuff::Grab(void)
+{
+ char *w=work;
+ work=0; wlen=0;
+ Flush();
+ return w;
+}
+
+bool cLineBuff::Check(int num)
+{
+ if(wlen-blen<num) {
+ if(num<blockSize) num=blockSize;
+ char *w=(char *)realloc(work,wlen+num);
+ if(w) {
+ wlen+=num;
+ work=w;
+ }
+ }
+ return work!=0;
+}
+
+void cLineBuff::Printf(const char *fmt, ...)
+{
+ int q=120;
+ while(Check(q)) {
+ int s=wlen-blen;
+ va_list ap;
+ va_start(ap,fmt);
+ q=vsnprintf(work+blen,s,fmt,ap);
+ va_end(ap);
+ if(q<s) {
+ if(q>0) blen+=q;
+ break;
+ }
+ // truncated
+ q+=100;
+ }
+}
+
+void cLineBuff::Strcat(const char *str)
+{
+ if(str) Printf("%s",str);
+}
+
+void cLineBuff::Back(int n)
+{
+ if(n>blen) blen=0; else blen-=n;
+ if(work) work[blen]=0;
+}
+
+// -- cSimpleListBase --------------------------------------------------------------
+
+cSimpleListBase::cSimpleListBase(void)
+{
+ first=last=0; count=0;
+}
+
+cSimpleListBase::~cSimpleListBase()
+{
+ Clear();
+}
+
+void cSimpleListBase::Add(cSimpleItem *Item, cSimpleItem *After)
+{
+ if(After) {
+ Item->next=After->next;
+ After->next=Item;
+ }
+ else {
+ Item->next=0;
+ if(last) last->next=Item;
+ else first=Item;
+ }
+ if(!Item->next) last=Item;
+ count++;
+}
+
+void cSimpleListBase::Ins(cSimpleItem *Item)
+{
+ Item->next=first;
+ first=Item;
+ if(!Item->next) last=Item;
+ count++;
+}
+
+void cSimpleListBase::Del(cSimpleItem *Item, bool Del)
+{
+ if(first==Item) {
+ first=Item->next;
+ if(!first) last=0;
+ }
+ else {
+ cSimpleItem *item=first;
+ while(item) {
+ if(item->next==Item) {
+ item->next=Item->next;
+ if(!item->next) last=item;
+ break;
+ }
+ item=item->next;
+ }
+ }
+ count--;
+ if(Del) delete Item;
+}
+
+void cSimpleListBase::Clear(void)
+{
+ while(first) Del(first);
+ first=last=0; count=0;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___MISC_H
+#define ___MISC_H
+
+// ----------------------------------------------------------------
+
+#define WORD(buffer,index,mask) (((buffer[(index)]<<8) + buffer[(index)+1]) & mask)
+#define SCT_LEN(sct) (3+(((sct)[1]&0x0f)<<8)+(sct)[2])
+
+// ----------------------------------------------------------------
+
+#define DEV_DVB_FRONTEND "frontend"
+#define DEV_DVB_DVR "dvr"
+#define DEV_DVB_DEMUX "demux"
+#define DEV_DVB_CA "ca"
+void DvbName(const char *Name, int n, char *buffer, int len);
+int DvbOpen(const char *Name, int n, int Mode, bool ReportError=false);
+
+const char *HexStr(char *str, const unsigned char *mem, int len);
+#define KeyStr(str,key) HexStr(str,key,8)
+
+int GetHex(const char * &line, unsigned char *store, int count, bool fixedLen=true);
+int GetHexAsc(const char * &line, unsigned char *store, int count);
+int GetChar(const char * &line, int *store, int count);
+unsigned long long Bin2LongLong(unsigned char *mem, int len);
+#define Bin2Int(mem,len) ((int)Bin2LongLong((mem),(len)))
+
+bool CheckNull(const unsigned char *data, int len);
+bool CheckFF(const unsigned char *data, int len);
+unsigned char XorSum(const unsigned char *mem, int len);
+unsigned int crc32_le(unsigned int crc, unsigned char const *p, int len);
+
+// ----------------------------------------------------------------
+
+class cLineBuff {
+private:
+ int blockSize;
+ char *work;
+ int blen, wlen;
+ //
+ bool Check(int num);
+protected:
+ char *Grab(void);
+public:
+ cLineBuff(int blocksize);
+ ~cLineBuff();
+ void Printf(const char *fmt, ...) __attribute__ ((format (printf,2,3)));
+ void Strcat(const char *str);
+ void Flush(void);
+ void Back(int n);
+ const char *Line(void) const { return work; }
+ int Length(void) const { return blen; }
+ };
+
+// ----------------------------------------------------------------
+
+class cSimpleListBase;
+
+class cSimpleItem {
+friend class cSimpleListBase;
+private:
+ cSimpleItem *next;
+public:
+ virtual ~cSimpleItem() {}
+ cSimpleItem *Next(void) const { return next; }
+ };
+
+class cSimpleListBase {
+protected:
+ cSimpleItem *first, *last;
+ int count;
+public:
+ cSimpleListBase(void);
+ ~cSimpleListBase();
+ void Add(cSimpleItem *Item, cSimpleItem *After=0);
+ void Ins(cSimpleItem *Item);
+ void Del(cSimpleItem *Item, bool Del=true);
+ void Clear(void);
+ int Count(void) const { return count; }
+ };
+
+template<class T> class cSimpleList : public cSimpleListBase {
+public:
+ T *First(void) const { return (T *)first; }
+ T *Last(void) const { return (T *)last; }
+ T *Next(const T *item) const { return (T *)item->cSimpleItem::Next(); }
+ };
+
+#endif //___MISC_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+
+#include "network.h"
+#include "misc.h"
+#include "log-core.h"
+
+const char *netscript=0;
+int netTimeout=60*1000; // timeout for shutting down dialup-network
+
+// -- cNetWatcher ---------------------------------------------------------------
+
+class cNetWatcher : protected cThread {
+private:
+ int count;
+ cTimeMs down;
+ bool netup, downDelay;
+ cSimpleList<cNetSocket> socks;
+ //
+ int RunCommand(const char *cmd, const char *state);
+protected:
+ virtual void Action(void);
+public:
+ cNetWatcher(void);
+ ~cNetWatcher();
+ void Up(cNetSocket *so);
+ void Down(cNetSocket *so);
+ void Block(void) { Lock(); }
+ void Unblock(void) { Unlock(); }
+ };
+
+static cNetWatcher nw;
+
+cNetWatcher::cNetWatcher(void)
+:cThread("Netwatcher")
+{
+ count=0; netup=downDelay=false;
+}
+
+cNetWatcher::~cNetWatcher()
+{
+ Cancel(2);
+ Lock();
+ if(netup) RunCommand(netscript,"down");
+ cNetSocket *so;
+ while((so=socks.First())) socks.Del(so,false);
+ Unlock();
+}
+
+void cNetWatcher::Up(cNetSocket *so)
+{
+ Lock();
+ socks.Add(so);
+ if(!Active()) Start();
+ if(netscript) {
+ downDelay=false; netup=true;
+ RunCommand(netscript,"up");
+ count++;
+ PRINTF(L_CORE_NET,"netwatch up (%d)",count);
+ }
+ else PRINTF(L_CORE_NET,"netwatch up");
+ Unlock();
+}
+
+void cNetWatcher::Down(cNetSocket *so)
+{
+ Lock();
+ socks.Del(so,false);
+ if(netscript) {
+ if(--count==0) {
+ downDelay=true;
+ down.Set(netTimeout);
+ }
+ PRINTF(L_CORE_NET,"netwatch down (%d)",count);
+ }
+ else PRINTF(L_CORE_NET,"netwatch down");
+ Unlock();
+}
+
+void cNetWatcher::Action(void)
+{
+ while(Running()) {
+ sleep(1);
+ Lock();
+ if(downDelay && down.TimedOut()) {
+ PRINTF(L_CORE_NET,"netdown timeout expired");
+ if(netup) {
+ RunCommand(netscript,"down");
+ netup=false;
+ }
+ downDelay=false;
+ }
+ for(cNetSocket *so=socks.First(); so;) {
+ cNetSocket *next=socks.Next(so);
+ so->Lock();
+ if(so->connected && so->activity.TimedOut()) {
+ PRINTF(L_CORE_NET,"idle timeout, disconnected %s:%d",so->hostname,so->port);
+ so->Disconnect();
+ }
+ so->Unlock();
+ so=next;
+ }
+ Unlock();
+ }
+}
+
+int cNetWatcher::RunCommand(const char *cmd, const char *state)
+{
+ char *tmp;
+ asprintf(&tmp,"%s %s",cmd,state);
+ PRINTF(L_CORE_NET,"netwatch cmd exec '%s'",tmp);
+ int res=SystemExec(tmp);
+ free(tmp);
+ return res;
+}
+
+// -- cNetSocket ------------------------------------------------------------------
+
+cNetSocket::cNetSocket(int ConnectTimeout, int ReadWriteTimeout, int IdleTimeout, bool Udp)
+{
+ hostname=0; sd=-1; connected=netup=false;
+ udp=Udp; conTimeout=ConnectTimeout; rwTimeout=ReadWriteTimeout;
+ idleTimeout=IdleTimeout*1000;
+}
+
+cNetSocket::~cNetSocket()
+{
+ Disconnect();
+ free(hostname);
+}
+
+void cNetSocket::Activity(void)
+{
+ activity.Set(idleTimeout);
+}
+
+bool cNetSocket::GetAddr(struct sockaddr_in *saddr, const char *Hostname, int Port)
+{
+ const struct hostent * const hostaddr=gethostbyname(Hostname);
+ if(hostaddr) {
+ saddr->sin_family=AF_INET;
+ saddr->sin_port=htons(Port);
+ saddr->sin_addr.s_addr=((struct in_addr *)hostaddr->h_addr)->s_addr;
+ return true;
+ }
+ PRINTF(L_GEN_ERROR,"socket: name lookup error for %s",Hostname);
+ return false;
+}
+
+int cNetSocket::GetSocket(bool Udp)
+{
+ int proto=0;
+ const struct protoent * const ptrp=getprotobyname(Udp?"udp":"tcp");
+ if(ptrp) proto=ptrp->p_proto;
+ int so;
+ if((so=socket(PF_INET,Udp?SOCK_DGRAM:SOCK_STREAM,proto))>=0) {
+ int flgs=fcntl(so,F_GETFL);
+ if(flgs>=0) {
+ if(fcntl(so,F_SETFL,flgs|O_NONBLOCK)==0)
+ return so;
+ else PRINTF(L_GEN_ERROR,"socket: fcntl SETFL failed: %s",strerror(errno));
+ }
+ else PRINTF(L_GEN_ERROR,"socket: fcntl GETFL failed: %s",strerror(errno));
+ close(so);
+ }
+ else PRINTF(L_GEN_ERROR,"socket: socket failed: %s",strerror(errno));
+ return -1;
+}
+
+bool cNetSocket::Connect(const char *Hostname, int Port, int timeout)
+{
+ nw.Block();
+ Lock();
+ Disconnect();
+ if(Hostname) {
+ free(hostname);
+ hostname=strdup(Hostname); port=Port;
+ }
+ if(timeout<0) timeout=conTimeout;
+ nw.Up(this); netup=true;
+ nw.Unblock();
+ struct sockaddr_in socketAddr;
+ if(GetAddr(&socketAddr,hostname,port) && (sd=GetSocket(udp))>=0) {
+ PRINTF(L_CORE_NET,"connecting to %s:%d/%s (%d.%d.%d.%d)",
+ hostname,port,udp?"udp":"tcp",
+ (socketAddr.sin_addr.s_addr>> 0)&0xff,(socketAddr.sin_addr.s_addr>> 8)&0xff,(socketAddr.sin_addr.s_addr>>16)&0xff,(socketAddr.sin_addr.s_addr>>24)&0xff);
+ if(connect(sd,(struct sockaddr *)&socketAddr,sizeof(socketAddr))==0)
+ connected=true;
+ else if(errno==EINPROGRESS) {
+ if(Select(false,timeout)>0) {
+ int r=-1;
+ unsigned int l=sizeof(r);
+ if(getsockopt(sd,SOL_SOCKET,SO_ERROR,&r,&l)==0) {
+ if(r==0)
+ connected=true;
+ else PRINTF(L_GEN_ERROR,"socket: connect failed (late): %s",strerror(r));
+ }
+ else PRINTF(L_GEN_ERROR,"socket: getsockopt failed: %s",strerror(errno));
+ }
+ else PRINTF(L_GEN_ERROR,"socket: connect timed out");
+ }
+ else PRINTF(L_GEN_ERROR,"socket: connect failed: %s",strerror(errno));
+
+ if(connected) { Activity(); Unlock(); return true; }
+ }
+ Unlock();
+ Disconnect();
+ return false;
+}
+
+
+bool cNetSocket::Bind(const char *Hostname, int Port)
+{
+ nw.Block();
+ Lock();
+ Disconnect();
+ if(Hostname) {
+ free(hostname);
+ hostname=strdup(Hostname); port=Port;
+ }
+ nw.Up(this); netup=true;
+ nw.Unblock();
+ struct sockaddr_in socketAddr;
+ if(GetAddr(&socketAddr,hostname,port) && (sd=GetSocket(udp))>=0) {
+ PRINTF(L_CORE_NET,"socket: binding to %s:%d/%s (%d.%d.%d.%d)",
+ hostname,port,udp?"udp":"tcp",
+ (socketAddr.sin_addr.s_addr>> 0)&0xff,(socketAddr.sin_addr.s_addr>> 8)&0xff,(socketAddr.sin_addr.s_addr>>16)&0xff,(socketAddr.sin_addr.s_addr>>24)&0xff);
+ if(bind(sd,(struct sockaddr *)&socketAddr,sizeof(socketAddr))==0) {
+ connected=true;
+ Activity(); Unlock();
+ return true;
+ }
+ else PRINTF(L_GEN_ERROR,"socket: bind failed: %s",strerror(errno));
+ }
+ Unlock();
+ Disconnect();
+ return false;
+}
+
+void cNetSocket::Disconnect(void)
+{
+ nw.Block();
+ cMutexLock lock(this);
+ if(sd>=0) { close(sd); sd=-1; }
+ connected=false;
+ if(netup) { nw.Down(this); netup=false; }
+ nw.Unblock();
+}
+
+void cNetSocket::Flush(void)
+{
+ cMutexLock lock(this);
+ if(sd>=0) {
+ unsigned char buff[512];
+ while(read(sd,buff,sizeof(buff))>0) Activity();
+ }
+}
+
+int cNetSocket::Read(unsigned char *data, int len, int timeout)
+{
+ cMutexLock lock(this);
+ if(timeout<0) timeout=rwTimeout;
+ int r=Select(true,timeout);
+ if(r>0) {
+ r=read(sd,data,len);
+ if(r<0) PRINTF(L_GEN_ERROR,"socket: read failed: %s",strerror(errno));
+ else if(r>0) HEXDUMP(L_CORE_NETDATA,data,r,"network read");
+ }
+ Activity();
+ return r;
+}
+
+int cNetSocket::Write(const unsigned char *data, int len, int timeout)
+{
+ cMutexLock lock(this);
+ if(timeout<0) timeout=rwTimeout;
+ int r=Select(false,timeout);
+ if(r>0) {
+ r=write(sd,data,len);
+ if(r<0) PRINTF(L_GEN_ERROR,"socket: write failed: %s",strerror(errno));
+ else if(r>0) HEXDUMP(L_CORE_NETDATA,data,r,"network write");
+ }
+ Activity();
+ return r;
+}
+
+int cNetSocket::SendTo(const char *Host, int Port, const unsigned char *data, int len, int timeout)
+{
+ cMutexLock lock(this);
+ if(timeout<0) timeout=rwTimeout;
+ int r=Select(false,timeout);
+ if(r>0) {
+ struct sockaddr_in saddr;
+ if(GetAddr(&saddr,Host,Port)) {
+ r=sendto(sd,data,len,0,(struct sockaddr *)&saddr,sizeof(saddr));
+ if(r<0) PRINTF(L_GEN_ERROR,"socket: sendto %d.%d.%d.%d:%d failed: %s",(saddr.sin_addr.s_addr>> 0)&0xff,(saddr.sin_addr.s_addr>> 8)&0xff,(saddr.sin_addr.s_addr>>16)&0xff,(saddr.sin_addr.s_addr>>24)&0xff,Port,strerror(errno));
+ else if(r>0) HEXDUMP(L_CORE_NETDATA,data,r,"network sendto %d.%d.%d.%d:%d",(saddr.sin_addr.s_addr>> 0)&0xff,(saddr.sin_addr.s_addr>> 8)&0xff,(saddr.sin_addr.s_addr>>16)&0xff,(saddr.sin_addr.s_addr>>24)&0xff,Port);
+ }
+ else r=-1;
+ }
+ Activity();
+ return r;
+}
+
+int cNetSocket::Select(bool forRead, int timeout)
+{
+ if(sd>=0) {
+ fd_set fds;
+ FD_ZERO(&fds); FD_SET(sd,&fds);
+ struct timeval tv;
+ tv.tv_sec=timeout; tv.tv_usec=0;
+ int r=select(sd+1,forRead ? &fds:0,forRead ? 0:&fds,0,&tv);
+ if(r>0) return 1;
+ else if(r<0) {
+ PRINTF(L_GEN_ERROR,"socket: select failed: %s",strerror(errno));
+ return -1;
+ }
+ else {
+ if(timeout>0) PRINTF(L_CORE_NET,"socket: select timed out (%d secs)",timeout);
+ return 0;
+ }
+ }
+ return -1;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___NETWORK_H
+#define ___NETWORK_H
+
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+#include "misc.h"
+
+// ----------------------------------------------------------------
+
+#define DEFAULT_CONNECT_TIMEOUT 20
+#define DEFAULT_READWRITE_TIMEOUT 3
+#define DEFAULT_IDLE_TIMEOUT 120
+
+extern const char *netscript;
+extern int netTimeout;
+
+// ----------------------------------------------------------------
+
+class cNetWatcher;
+
+class cNetSocket : public cSimpleItem, private cMutex {
+friend class cNetWatcher;
+private:
+ int sd;
+ char *hostname;
+ int port, dummy, conTimeout, rwTimeout, idleTimeout;
+ bool udp, connected, netup;
+ cTimeMs activity;
+ //
+ int Select(bool forRead, int timeout);
+ void Activity(void);
+ bool GetAddr(struct sockaddr_in *saddr, const char *Hostname, int Port);
+ int GetSocket(bool Udp);
+public:
+ cNetSocket(int ConnectTimeout, int ReadWriteTimeout, int IdleTimeout, bool Udp=false);
+ ~cNetSocket();
+ bool Connect(const char *Hostname, int Port, int timeout=-1);
+ bool Bind(const char *Hostname, int Port);
+ void Disconnect(void);
+ int Read(unsigned char *data, int len, int timeout=-1);
+ int Write(const unsigned char *data, int len, int timeout=-1);
+ int SendTo(const char *Host, int Port, const unsigned char *data, int len, int timeout=-1);
+ void Flush(void);
+ bool Connected(void) { return connected; }
+ };
+
+#endif //___NETWORK_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___OPENSSL_COMPAT_H
+#define ___OPENSSL_COMPAT_H
+
+#include <openssl/opensslv.h>
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090800fL || OPENSSL_VERSION_NUMBER < 0x0090705fL
+#define DES_CAST(x) ((DES_cblock *)(x))
+#else
+#define DES_CAST(x) (x)
+#endif
+
+#endif
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___OPTS_H
+#define ___OPTS_H
+
+class cOsdMenu;
+
+// ----------------------------------------------------------------
+
+class cOpt {
+private:
+ char *fullname;
+ bool persistant;
+protected:
+ const char *name, *title;
+ //
+ const char *FullName(const char *PreStr);
+public:
+ cOpt(const char *Name, const char *Title);
+ virtual ~cOpt();
+ virtual void Parse(const char *Value)=0;
+ virtual void Backup(void)=0;
+ virtual bool Set(void)=0;
+ virtual void Store(const char *PreStr)=0;
+ virtual void Create(cOsdMenu *menu)=0;
+ const char *Name(void) { return name; }
+ void Persistant(bool per) { persistant=per; }
+ bool Persistant(void) { return persistant; }
+ };
+
+// ----------------------------------------------------------------
+
+class cOptInt : public cOpt {
+protected:
+ int *storage, value, min, max;
+public:
+ cOptInt(const char *Name, const char *Title, int *Storage, int Min, int Max);
+ virtual void Parse(const char *Value);
+ virtual void Backup(void);
+ virtual bool Set(void);
+ virtual void Store(const char *PreStr);
+ virtual void Create(cOsdMenu *menu);
+ };
+
+// ----------------------------------------------------------------
+
+class cOptSel : public cOptInt {
+private:
+ const char **trStrings;
+protected:
+ const char * const *strings;
+public:
+ cOptSel(const char *Name, const char *Title, int *Storage, int NumStr, const char * const *Strings);
+ virtual ~cOptSel();
+ virtual void Create(cOsdMenu *menu);
+ };
+
+// ----------------------------------------------------------------
+
+class cOptBool : public cOptInt {
+public:
+ cOptBool(const char *Name, const char *Title, int *Storage);
+ virtual void Create(cOsdMenu *menu);
+ };
+
+// ----------------------------------------------------------------
+
+class cOptStr : public cOpt {
+protected:
+ char *storage, *value;
+ const char *allowed;
+ int size;
+public:
+ cOptStr(const char *Name, const char *Title, char *Storage, int Size, const char *Allowed);
+ virtual ~cOptStr();
+ virtual void Parse(const char *Value);
+ virtual void Backup(void);
+ virtual bool Set(void);
+ virtual void Store(const char *PreStr);
+ virtual void Create(cOsdMenu *menu);
+ };
+
+// ----------------------------------------------------------------
+
+class cOpts {
+private:
+ int numOpts, numAdd;
+ const char *preStr;
+ cOpt **opts;
+public:
+ cOpts(const char *PreStr, int NumOpts);
+ ~cOpts();
+ void Add(cOpt *opt);
+ bool Parse(const char *Name, const char *Value);
+ bool Store(bool AsIs);
+ void Backup(void);
+ void Create(cOsdMenu *menu);
+ };
+
+#endif //___OPTS_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "parse.h"
+#include "misc.h"
+#include "log.h"
+
+// -----------------------------------------------------------------------------
+
+void SetSctLen(unsigned char *data, int len)
+{
+ data[1]=(len>>8) | 0x70;
+ data[2]=len & 0xFF;
+}
+
+static void SortNanos(unsigned char *dest, const unsigned char *src, int len)
+{
+ int w=0, c=-1;
+ while(1) {
+ int n=0x100;
+ for(int j=0; j<len;) {
+ int l=src[j+1]+2;
+ if(src[j]==c) {
+ if(w+l>len) {
+ PRINTF(L_GEN_ERROR,"sortnanos: sanity check failed. Exceeding memory area. Probably corrupted nanos!");
+ memset(dest,0,len); // zero out everything
+ return;
+ }
+ memcpy(&dest[w],&src[j],l);
+ w+=l;
+ }
+ else if(src[j]>c && src[j]<n)
+ n=src[j];
+ j+=l;
+ }
+ if(n==0x100) break;
+ c=n;
+ }
+}
+
+// -- cProviders ---------------------------------------------------------------
+
+void cProviders::AddProv(cProvider *p)
+{
+ if(p) Add(p);
+}
+
+cProvider *cProviders::FindProv(const unsigned char *data)
+{
+ cProvider *p=First();
+ while(p) {
+ if(p->MatchID(data)) break;
+ p=Next(p);
+ }
+ return p;
+}
+
+cProvider *cProviders::MatchProv(const unsigned char *data)
+{
+ cProvider *p=First();
+ while(p) {
+ if(p->MatchEMM(data)) break;
+ p=Next(p);
+ }
+ return p;
+}
+
+// -- cAssSct ------------------------------------------------------------------
+
+cAssSct::cAssSct(const unsigned char *Data)
+{
+ data=Data;
+}
+
+// -- cAssembleData ------------------------------------------------------------
+
+cAssembleData::cAssembleData(const unsigned char *Data)
+{
+ data=Data;
+ curr=0;
+}
+
+void cAssembleData::SetAssembled(const unsigned char *Data)
+{
+ Add(new cAssSct(Data));
+ curr=First();
+}
+
+const unsigned char *cAssembleData::Assembled(void)
+{
+ const unsigned char *ret=0;
+ if(First()) {
+ if(curr) {
+ ret=curr->Data();
+ curr=Next(curr);
+ }
+ }
+ else {
+ ret=data;
+ data=0;
+ }
+ return ret;
+}
+
+// -- cIdSet -------------------------------------------------------------------
+
+cIdSet::cIdSet(void)
+{
+ card=0;
+}
+
+cIdSet::~cIdSet()
+{
+ delete card;
+}
+
+void cIdSet::ResetIdSet(void)
+{
+ delete card; card=0;
+ Clear();
+}
+
+void cIdSet::SetCard(cCard *c)
+{
+ delete card;
+ card=c;
+}
+
+bool cIdSet::MatchEMM(const unsigned char *data)
+{
+ return (card && card->MatchEMM(data)) || MatchProv(data);
+}
+
+bool cIdSet::MatchAndAssemble(cAssembleData *ad, int *updtype, cProvider **prov)
+{
+ const unsigned char *data=ad->Data();
+ cProvider *p;
+ if(card && card->MatchEMM(data)) {
+ if(updtype) *updtype=card->UpdateType(data);
+ if(prov) *prov=0;
+ if(card->Assemble(ad)>=0) return true;
+ }
+ else if((p=MatchProv(data))) {
+ if(updtype) *updtype=p->UpdateType(data);
+ if(prov) *prov=p;
+ if(p->Assemble(ad)>=0) return true;
+ }
+ return false;
+}
+
+// -- cProviderIrdeto ----------------------------------------------------------
+
+cProviderIrdeto::cProviderIrdeto(unsigned char pb, const unsigned char *pi)
+{
+ provBase=pb;
+ memcpy(provId,pi,sizeof(provId));
+}
+
+bool cProviderIrdeto::MatchID(const unsigned char *data)
+{
+ const unsigned int mode=cParseIrdeto::AddrBase(data);
+ return (mode<0x10 && mode==provBase);
+}
+
+bool cProviderIrdeto::MatchEMM(const unsigned char *data)
+{
+ const unsigned int len=cParseIrdeto::AddrLen(data);
+ const unsigned int mode=cParseIrdeto::AddrBase(data);
+ return ( mode<0x10 && len<=sizeof(provId)
+ && mode==provBase && (!len || !memcmp(&data[4],provId,len)));
+}
+
+unsigned long long cProviderIrdeto::ProvId(void)
+{
+ return (provId[0]<<24)+(provId[1]<<16)+(provId[2]<<8)+provBase;
+}
+
+// -- cCardIrdeto --------------------------------------------------------------
+
+cCardIrdeto::cCardIrdeto(unsigned char hb, const unsigned char *hs)
+{
+ hexBase=hb;
+ memcpy(hexSer,hs,sizeof(hexSer));
+}
+
+bool cCardIrdeto::MatchEMM(const unsigned char *data)
+{
+ const unsigned int len=cParseIrdeto::AddrLen(data);
+ const unsigned int mode=cParseIrdeto::AddrBase(data);
+ return ( mode>=0x10 && len<=sizeof(hexSer)
+ && mode==hexBase && (!len || !memcmp(&data[4],hexSer,len)));
+}
+
+int cCardIrdeto::UpdateType(const unsigned char *data)
+{
+ switch(cParseIrdeto::AddrLen(data)) {
+ case 0: return 0; // global
+ case 1:
+ case 2: return 2; // shared
+ default: return 3; // unique
+ }
+}
+
+// -- cParseIrdeto -------------------------------------------------------------
+
+unsigned int cParseIrdeto::AddrLen(const unsigned char *data)
+{
+ return data[3] & 0x07;
+}
+
+unsigned int cParseIrdeto::AddrBase(const unsigned char *data)
+{
+ return data[3] >> 3;
+}
+
+// -- cProviderSeca ------------------------------------------------------------
+
+cProviderSeca::cProviderSeca(const unsigned char *pi, const unsigned char *s)
+{
+ memcpy(provId,pi,sizeof(provId));
+ memcpy(sa,s,sizeof(sa));
+}
+
+bool cProviderSeca::MatchID(const unsigned char *data)
+{
+ const unsigned char *id=cParseSeca::ProvIdPtr(data);
+ return id && !memcmp(id,provId,sizeof(provId));
+}
+
+bool cProviderSeca::MatchEMM(const unsigned char *data)
+{
+ return TID(data)==0x84 &&
+ !memcmp(SID(data),provId,sizeof(provId)) && !memcmp(SA(data),sa,sizeof(sa));
+}
+
+unsigned long long cProviderSeca::ProvId(void)
+{
+ return (provId[0]<<8)+provId[1];
+}
+
+// -- cCardSeca ----------------------------------------------------------------
+
+cCardSeca::cCardSeca(const unsigned char *u)
+{
+ memcpy(ua,u,sizeof(ua));
+}
+
+bool cCardSeca::MatchEMM(const unsigned char *data)
+{
+ return TID(data)==0x82 &&
+ !memcmp(UA(data),ua,sizeof(ua));
+}
+
+// -- cParseSeca ---------------------------------------------------------------
+
+int cParseSeca::CmdLen(const unsigned char *data)
+{
+ struct SecaCmd *s=(struct SecaCmd *)data;
+ return ((s->cmdLen1&0x0f)<<8) | s->cmdLen2;
+}
+
+int cParseSeca::Payload(const unsigned char *data, const unsigned char **payload)
+{
+ int l;
+ switch(TID(data)) {
+ case 0x80:
+ case 0x81: l=sizeof(struct SecaEcm); break;
+ case 0x82: l=sizeof(struct SecaEmmUnique); break;
+ case 0x84: l=sizeof(struct SecaEmmShared); break;
+ default: return -1;
+ }
+ if(payload) *payload=&data[l];
+ return CmdLen(data)-l+sizeof(struct SecaCmd);
+}
+
+int cParseSeca::SysMode(const unsigned char *data)
+{
+ switch(TID(data)) {
+ case 0x80:
+ case 0x81: return ((struct SecaEcm *)data)->sm; break;
+ case 0x82: return ((struct SecaEmmUnique *)data)->sm; break;
+ case 0x84: return ((struct SecaEmmShared *)data)->sm; break;
+ default: return -1;
+ }
+}
+
+int cParseSeca::KeyNr(const unsigned char *data)
+{
+ switch(TID(data)) {
+ case 0x80:
+ case 0x81: return ((struct SecaEcm *)data)->keyNr; break;
+ case 0x82: return ((struct SecaEmmUnique *)data)->keyNr; break;
+ case 0x84: return ((struct SecaEmmShared *)data)->keyNr; break;
+ default: return -1;
+ }
+}
+
+const unsigned char *cParseSeca::ProvIdPtr(const unsigned char *data)
+{
+ switch(TID(data)) {
+ case 0x80:
+ case 0x81: return ((struct SecaEcm *)data)->id;
+ case 0x82: return ((struct SecaEmmUnique *)data)->id;
+ case 0x84: return ((struct SecaEmmShared *)data)->id;
+ }
+ return 0;
+}
+
+int cParseSeca::ProvId(const unsigned char *data)
+{
+ const unsigned char *id=cParseSeca::ProvIdPtr(data);
+ return id ? (id[0]<<8)+id[1] : -1;
+}
+
+// -- cProviderViaccess --------------------------------------------------------
+
+cProviderViaccess::cProviderViaccess(void)
+{
+ sharedEmm=0; sharedLen=sharedToggle=0;
+}
+
+cProviderViaccess::cProviderViaccess(const unsigned char *id, const unsigned char *s)
+{
+ sharedEmm=0; sharedLen=sharedToggle=0;
+ memcpy(ident,id,sizeof(ident));
+ ident[2]&=0xf0;
+ memcpy(sa,s,sizeof(sa));
+}
+
+cProviderViaccess::~cProviderViaccess()
+{
+ free(sharedEmm);
+}
+
+bool cProviderViaccess::MatchID(const unsigned char *data)
+{
+ const unsigned char *id=cParseViaccess::ProvIdPtr(data);
+ return id && id[0]==ident[0] && id[1]==ident[1] && (id[2]&0xf0)==ident[2];
+}
+
+bool cProviderViaccess::MatchEMM(const unsigned char *data)
+{
+ switch(data[0]) {
+ case 0x8e:
+ if(memcmp(&data[3],sa,sizeof(sa)-1)) break;
+ if((data[6]&2)==0)
+ return sharedEmm && MatchID(sharedEmm);
+ // fall through
+ case 0x8c:
+ case 0x8d:
+ return MatchID(data);
+ }
+ return false;
+}
+
+unsigned long long cProviderViaccess::ProvId(void)
+{
+ return (ident[0]<<16)+(ident[1]<<8)+ident[2];
+}
+
+int cProviderViaccess::UpdateType(const unsigned char *data)
+{
+ switch(data[0]) {
+ case 0x8e: return 2; // shared
+ case 0x8c:
+ case 0x8d:
+ default: return 0; // global
+ }
+}
+
+int cProviderViaccess::Assemble(cAssembleData *ad)
+{
+ const unsigned char *data=ad->Data();
+ int len=SCT_LEN(data);
+ switch(data[0]) {
+ case 0x8C:
+ case 0x8D:
+ if(data[0]!=sharedToggle) {
+ free(sharedEmm);
+ sharedLen=len;
+ sharedEmm=(unsigned char *)malloc(len);
+ if(sharedEmm) memcpy(sharedEmm,data,sharedLen);
+ sharedToggle=data[0];
+ }
+ break;
+
+ case 0x8E:
+ if(sharedEmm) {
+ unsigned char tmp[len+sharedLen];
+ unsigned char *ass=(unsigned char *)cParseViaccess::NanoStart(data);
+ len-=(ass-data);
+ if((data[6]&2)==0) {
+ const int addrlen=len-8;
+ len=0;
+ tmp[len++]=0x9e;
+ tmp[len++]=addrlen;
+ memcpy(&tmp[len],&ass[0],addrlen); len+=addrlen;
+ tmp[len++]=0xf0;
+ tmp[len++]=0x08;
+ memcpy(&tmp[len],&ass[addrlen],8); len+=8;
+ }
+ else {
+ memcpy(tmp,ass,len);
+ }
+ ass=(unsigned char *)cParseViaccess::NanoStart(sharedEmm);
+ int l=sharedLen-(ass-sharedEmm);
+ memcpy(&tmp[len],ass,l); len+=l;
+
+ ass=(unsigned char *)malloc(len+7);
+ if(ass) {
+ memcpy(ass,data,7);
+ SortNanos(ass+7,tmp,len);
+ SetSctLen(ass,len+4);
+ ad->SetAssembled(ass);
+ return 1; // assembled
+ }
+ }
+ break;
+ }
+ return -1; // ignore
+}
+
+// -- cCardViaccess ------------------------------------------------------------
+
+cCardViaccess::cCardViaccess(const unsigned char *u)
+{
+ memcpy(ua,u,sizeof(ua));
+}
+
+bool cCardViaccess::MatchEMM(const unsigned char *data)
+{
+ return data[0]==0x88 && !memcmp(&data[3],ua,sizeof(ua));
+}
+
+// -- cParseViaccess -----------------------------------------------------------
+
+const unsigned char *cParseViaccess::NanoStart(const unsigned char *data)
+{
+ switch(data[0]) {
+ case 0x88: return &data[8];
+ case 0x8e: return &data[7];
+ case 0x8c:
+ case 0x8d: return &data[3];
+ case 0x80:
+ case 0x81: return &data[4];
+ }
+ return 0;
+}
+
+const unsigned char *cParseViaccess::CheckNano90(const unsigned char *data)
+{
+ return CheckNano90FromNano(NanoStart(data));
+}
+
+const unsigned char *cParseViaccess::CheckNano90FromNano(const unsigned char *data)
+{
+ if(data && data[0]==0x90 && data[1]==0x03) return data;
+ return 0;
+}
+
+int cParseViaccess::KeyNr(const unsigned char *data)
+{
+ return KeyNrFromNano(CheckNano90(data));
+}
+
+int cParseViaccess::KeyNrFromNano(const unsigned char *data)
+{
+ return data ? data[4]&0x0f : -1;
+}
+
+const unsigned char *cParseViaccess::ProvIdPtr(const unsigned char *data)
+{
+ data=CheckNano90(data);
+ return data ? &data[2] : 0;
+}
+
+int cParseViaccess::ProvId(const unsigned char *data)
+{
+ const unsigned char *id=cParseViaccess::ProvIdPtr(data);
+ return id ? (id[0]<<16)+(id[1]<<8)+(id[2]&0xf0) : -1;
+}
+
+// -- cCardNagra2 --------------------------------------------------------------
+
+cCardNagra2::cCardNagra2(const unsigned char *a)
+{
+ addr[0]=a[2];
+ addr[1]=a[1];
+ addr[2]=a[0];
+ addr[3]=a[3];
+}
+
+bool cCardNagra2::MatchEMM(const unsigned char *data)
+{
+ return data[0]==0x82 ||
+ (data[0]==0x83 && !memcmp(&data[3],addr,(data[7]==0x10)?3:4));
+}
+
+int cCardNagra2::UpdateType(const unsigned char *data)
+{
+ if(data[0]==0x83) return (data[7]==0x10) ? 2:3;
+ return 0;
+}
+
+// -- cProviderConax -----------------------------------------------------------
+
+cProviderConax::cProviderConax(const unsigned char *a)
+{
+ memcpy(addr,a,sizeof(addr));
+}
+
+bool cProviderConax::MatchID(const unsigned char *data)
+{
+ return MatchEMM(data);
+}
+
+bool cProviderConax::MatchEMM(const unsigned char *data)
+{
+ return !memcmp(&data[3],addr,sizeof(addr));
+}
+
+unsigned long long cProviderConax::ProvId(void)
+{
+ return Bin2LongLong(addr,sizeof(addr));
+}
+
+// -- cCardConax ---------------------------------------------------------------
+
+cCardConax::cCardConax(const unsigned char *a)
+{
+ memcpy(addr,a,sizeof(addr));
+}
+
+bool cCardConax::MatchEMM(const unsigned char *data)
+{
+ return !memcmp(&data[3],addr,sizeof(addr));
+}
+
+// -- cProviderCryptoworks -----------------------------------------------------
+
+cProviderCryptoworks::cProviderCryptoworks(const unsigned char *a)
+{
+ memcpy(addr,a,sizeof(addr));
+}
+
+bool cProviderCryptoworks::MatchID(const unsigned char *data)
+{
+ return MatchEMM(data);
+}
+
+bool cProviderCryptoworks::MatchEMM(const unsigned char *data)
+{
+ return false;
+}
+
+unsigned long long cProviderCryptoworks::ProvId(void)
+{
+ return Bin2LongLong(addr,sizeof(addr));
+}
+
+// -- cCardCryptoworks----------------------------------------------------------
+
+cCardCryptoworks::cCardCryptoworks(const unsigned char *a)
+{
+ memcpy(addr,a,sizeof(addr));
+ sharedEmm=0; sharedLen=globalToggle=0; globalCrc=0;
+}
+
+cCardCryptoworks::~cCardCryptoworks()
+{
+ free(sharedEmm);
+}
+
+bool cCardCryptoworks::MatchEMM(const unsigned char *data)
+{
+ if((data[1]&0xF0)==0x70 && data[3]==0xA9 && data[4]==0xff) {
+ switch(data[0]) {
+ case 0x82: return !memcmp(&data[5],addr,sizeof(addr));
+ case 0x84: return !memcmp(&data[5],addr,sizeof(addr)-1);
+ case 0x86:
+ case 0x88:
+ case 0x89: return true;
+ }
+ }
+ return false;
+}
+
+int cCardCryptoworks::UpdateType(const unsigned char *data)
+{
+ switch(data[0]) {
+ case 0x82: return 3; // unique
+ case 0x84:
+ case 0x86: return 2; // shared
+ default: return 0; // global
+ }
+}
+
+int cCardCryptoworks::Assemble(cAssembleData *ad)
+{
+ const unsigned char *data=ad->Data();
+ int len=SCT_LEN(data);
+ switch(data[0]) {
+ case 0x82:
+ return 0; // no assemble needed
+
+ case 0x84:
+ free(sharedEmm);
+ sharedEmm=(unsigned char *)malloc(len);
+ if(sharedEmm) {
+ memcpy(sharedEmm,data,len);
+ sharedLen=len;
+ }
+ break;
+
+ case 0x86:
+ if(sharedEmm) {
+ int alen=len-5 + sharedLen-12;
+ unsigned char tmp[alen];
+ memcpy(tmp,&data[5],len-5);
+ memcpy(tmp+len-5,&sharedEmm[12],sharedLen-12);
+
+ unsigned char *ass=(unsigned char *)malloc(alen+12);
+ if(!ass) return -1; // ignore
+ memcpy(ass,sharedEmm,12);
+ SortNanos(ass+12,tmp,alen);
+ SetSctLen(ass,alen+9);
+ free(sharedEmm); sharedEmm=0;
+ if(ass[11]==alen) { // sanity check
+ ad->SetAssembled(ass);
+ return 1; // assembled
+ }
+ }
+ break;
+
+ case 0x88:
+ case 0x89:
+ if(data[0]!=globalToggle) {
+ globalToggle=data[0];
+ unsigned int crc=crc32_le(0,data+1,len-1);
+ if(crc!=globalCrc) {
+ globalCrc=crc;
+ return 0; // no assemble needed
+ }
+ }
+ break;
+ }
+ return -1; // ignore
+}
+
+// -- cProviderNDS -------------------------------------------------------------
+
+cProviderNDS::cProviderNDS(const unsigned char *s)
+{
+ memcpy(sa,s,sizeof(sa));
+}
+
+bool cProviderNDS::MatchID(const unsigned char *data)
+{
+ return MatchEMM(data);
+}
+
+bool cProviderNDS::MatchEMM(const unsigned char *data)
+{
+ return cParseNDS::HasAddr(data,sa);
+}
+
+unsigned long long cProviderNDS::ProvId(void)
+{
+ return Bin2LongLong(sa,sizeof(sa));
+}
+
+int cProviderNDS::Assemble(cAssembleData *ad)
+{
+ return cParseNDS::Assemble(ad,sa);
+}
+
+// -- cCardNDS -----------------------------------------------------------------
+
+cCardNDS::cCardNDS(const unsigned char *u)
+{
+ memcpy(ua,u,sizeof(ua));
+}
+
+bool cCardNDS::MatchEMM(const unsigned char *data)
+{
+ return cParseNDS::HasAddr(data,ua);
+}
+
+int cCardNDS::Assemble(cAssembleData *ad)
+{
+ return cParseNDS::Assemble(ad,ua);
+}
+
+// -- cParseNDS ----------------------------------------------------------------
+
+unsigned int cParseNDS::NumAddr(const unsigned char *data)
+{
+ return ((data[3]&0x30)>>4)+1;
+}
+
+int cParseNDS::AddrMode(const unsigned char *data)
+{
+ switch(data[3]&0xC0) {
+ case 0x40: return 3;
+ case 0x80: return 2;
+ default: return 0;
+ }
+}
+
+bool cParseNDS::HasAddr(const unsigned char *data, const unsigned char *a)
+{
+ int s;
+ switch(AddrMode(data)) {
+ case 2: s=3; break;
+ case 3: s=4; break;
+ default: return true;
+ }
+ for(int l=NumAddr(data)-1; l>=0; l--) {
+ if(!memcmp(&data[l*4+4],a,s)) return true;
+ }
+ return false;
+}
+
+const unsigned char *cParseNDS::PayloadStart(const unsigned char *data)
+{
+ //return &data[4 + NumAddr(data)*4 + 2];
+ if(AddrMode(data)==0) return &data[4 + 2];
+ else return &data[4 + NumAddr(data)*4];
+}
+
+int cParseNDS::PayloadSize(const unsigned char *data)
+{
+ //return emm[2]+emm[3]+4-1+5;
+ int l=SCT_LEN(data);
+ if(AddrMode(data)==0) return l-(4 + 2);
+ else return l-(4 + NumAddr(data)*4);
+}
+
+int cParseNDS::Assemble(cAssembleData *ad, const unsigned char *a)
+{
+ const unsigned char *data=ad->Data();
+ int len=cParseNDS::PayloadSize(data);
+ const unsigned char *pl=cParseNDS::PayloadStart(data);
+ switch(cParseNDS::AddrMode(data)) {
+ case 0:
+ {
+ int n=*(pl-1) & 0x01;
+ for(int i=cParseNDS::NumAddr(data); i>0 && len>0; i--) {
+ int l;
+ if(n) {
+ l=(((*pl & 0x3F)<<8) + *(pl+1)) << 1;
+ pl+=2; len-=2;
+ }
+ else {
+ l=(*pl & 0x3F) << 1;
+ pl++; len--;
+ }
+ if(l>0 && l<=len) {
+ unsigned char *ass=(unsigned char *)malloc(len+4); if(!ass) return -1; // ignore
+ ass[0]=data[0];
+ ass[3]=data[3]&0x0F;
+ memcpy(&ass[4],pl,l);
+ SetSctLen(ass,l+1);
+ ad->SetAssembled(ass);
+ pl+=l; len-=l;
+ }
+ }
+ return 1; // assembled
+ }
+
+ case 2:
+ case 3:
+ {
+ unsigned char *ass=(unsigned char *)malloc(len+8); if(!ass) return -1; // ignore
+ ass[0]=data[0];
+ ass[3]=data[3]&0x0F;
+ memcpy(&ass[4],a,4);
+ memcpy(&ass[8],pl,len);
+ SetSctLen(ass,len+5);
+ ad->SetAssembled(ass);
+ return 1; // assembled
+ }
+ }
+ return -1; // ignore
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___PARSE_H
+#define ___PARSE_H
+
+#include "misc.h"
+
+// ----------------------------------------------------------------
+
+void SetSctLen(unsigned char *data, int len);
+
+// ----------------------------------------------------------------
+
+class cAssSct : public cSimpleItem {
+private:
+ const unsigned char *data;
+public:
+ cAssSct(const unsigned char *Data);
+ const unsigned char *Data(void) const { return data; }
+ };
+
+class cAssembleData : private cSimpleList<cAssSct> {
+private:
+ const unsigned char *data;
+ cAssSct *curr;
+public:
+ cAssembleData(const unsigned char *Data);
+ void SetAssembled(const unsigned char *Data);
+ const unsigned char *Assembled(void);
+ const unsigned char *Data(void) const { return data; }
+ };
+
+class cProvider : public cSimpleItem {
+public:
+ virtual bool MatchID(const unsigned char *data)=0;
+ virtual bool MatchEMM(const unsigned char *data)=0;
+ virtual unsigned long long ProvId(void)=0;
+ virtual int UpdateType(const unsigned char *data) { return 2; }
+ virtual int Assemble(cAssembleData *ad) { return 0; }
+ };
+
+class cProviders : public cSimpleList<cProvider> {
+public:
+ void AddProv(cProvider *p);
+ cProvider *FindProv(const unsigned char *data);
+ cProvider *MatchProv(const unsigned char *data);
+ };
+
+class cCard {
+public:
+ virtual ~cCard() {}
+ virtual bool MatchEMM(const unsigned char *data)=0;
+ virtual int UpdateType(const unsigned char *data) { return 3; }
+ virtual int Assemble(cAssembleData *ad) { return 0; }
+ };
+
+class cIdSet : public cProviders {
+protected:
+ cCard *card;
+public:
+ cIdSet(void);
+ ~cIdSet();
+ void SetCard(cCard *c);
+ void ResetIdSet(void);
+ bool MatchEMM(const unsigned char *data);
+ bool MatchAndAssemble(cAssembleData *ad, int *updtype, cProvider **p);
+ };
+
+// -- IRDETO ------------------------------------------------------
+
+class cProviderIrdeto : public cProvider {
+public:
+ unsigned char provBase;
+ unsigned char provId[3];
+ //
+ cProviderIrdeto(void) {}
+ cProviderIrdeto(unsigned char pb, const unsigned char *pi);
+ virtual bool MatchID(const unsigned char *data);
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual unsigned long long ProvId(void);
+ };
+
+class cCardIrdeto : public cCard {
+public:
+ unsigned char hexBase;
+ unsigned char hexSer[3];
+ //
+ cCardIrdeto(void) {}
+ cCardIrdeto(unsigned char hb, const unsigned char *hs);
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual int UpdateType(const unsigned char *data);
+ };
+
+class cParseIrdeto {
+public:
+ static unsigned int AddrLen(const unsigned char *data);
+ static unsigned int AddrBase(const unsigned char *data);
+ };
+
+// -- SECA --------------------------------------------------------
+
+struct SecaCmd {
+ unsigned char tid;
+ unsigned char cmdLen1;
+ unsigned char cmdLen2;
+ };
+
+struct SecaEcm {
+ struct SecaCmd cmd;
+ unsigned char id[2];
+ unsigned char unknown;
+ unsigned char sm;
+ unsigned char keyNr;
+ };
+
+struct SecaEmmShared {
+ struct SecaCmd cmd;
+ unsigned char id[2];
+ unsigned char sa[3];
+ unsigned char sm;
+ unsigned char keyNr;
+ };
+
+struct SecaEmmUnique {
+ struct SecaCmd cmd;
+ unsigned char ua[6];
+ unsigned char id[2];
+ unsigned char sm;
+ unsigned char keyNr;
+ };
+
+#define TID(x) (((struct SecaCmd *)(x))->tid)
+#define SA(x) (((struct SecaEmmShared *)(x))->sa)
+#define SID(x) (((struct SecaEmmShared *)(x))->id)
+#define UA(x) (((struct SecaEmmUnique *)(x))->ua)
+#define UID(x) (((struct SecaEmmUnique *)(x))->id)
+
+class cProviderSeca : public cProvider {
+public:
+ unsigned char provId[2];
+ unsigned char sa[3];
+ //
+ cProviderSeca(void) {}
+ cProviderSeca(const unsigned char *pi, const unsigned char *s);
+ virtual bool MatchID(const unsigned char *data);
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual unsigned long long ProvId(void);
+ };
+
+class cCardSeca : public cCard {
+public:
+ unsigned char ua[6];
+ //
+ cCardSeca(void) {}
+ cCardSeca(const unsigned char *u);
+ virtual bool MatchEMM(const unsigned char *data);
+ };
+
+class cParseSeca {
+public:
+ static int CmdLen(const unsigned char *data);
+ static int Payload(const unsigned char *data, const unsigned char **payload);
+ static int SysMode(const unsigned char *data);
+ static int KeyNr(const unsigned char *data);
+ static const unsigned char *ProvIdPtr(const unsigned char *data);
+ static int ProvId(const unsigned char *data);
+ };
+
+// -- VIACCESS -----------------------------------------------------
+
+class cProviderViaccess : public cProvider {
+private:
+ unsigned char *sharedEmm;
+ int sharedLen, sharedToggle;
+public:
+ unsigned char ident[3];
+ unsigned char sa[4];
+ //
+ cProviderViaccess(void);
+ cProviderViaccess(const unsigned char *id, const unsigned char *s);
+ virtual ~cProviderViaccess();
+ virtual bool MatchID(const unsigned char *data);
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual unsigned long long ProvId(void);
+ virtual int UpdateType(const unsigned char *data);
+ virtual int Assemble(cAssembleData *ad);
+ };
+
+class cCardViaccess : public cCard {
+public:
+ unsigned char ua[5];
+ //
+ cCardViaccess(void) {}
+ cCardViaccess(const unsigned char *u);
+ virtual bool MatchEMM(const unsigned char *data);
+ };
+
+class cParseViaccess {
+public:
+ static const unsigned char *NanoStart(const unsigned char *data);
+ static const unsigned char *CheckNano90(const unsigned char *data);
+ static const unsigned char *CheckNano90FromNano(const unsigned char *data);
+ static int KeyNr(const unsigned char *data);
+ static int KeyNrFromNano(const unsigned char *data);
+ static const unsigned char *ProvIdPtr(const unsigned char *data);
+ static int ProvId(const unsigned char *data);
+ };
+
+// -- NAGRA 2 ------------------------------------------------------
+
+class cCardNagra2 : public cCard {
+public:
+ unsigned char addr[4];
+ //
+ cCardNagra2(const unsigned char *a);
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual int UpdateType(const unsigned char *data);
+ };
+
+// -- CONAX --------------------------------------------------------
+
+class cProviderConax : public cProvider {
+public:
+ unsigned char addr[7];
+ //
+ cProviderConax(const unsigned char *a);
+ virtual bool MatchID(const unsigned char *data);
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual unsigned long long ProvId(void);
+ };
+
+class cCardConax : public cCard {
+public:
+ unsigned char addr[7];
+ //
+ cCardConax(const unsigned char *a);
+ virtual bool MatchEMM(const unsigned char *data);
+ };
+
+// -- CRYPTOWORKS --------------------------------------------------
+
+class cProviderCryptoworks : public cProvider {
+public:
+ unsigned char addr[5];
+ //
+ cProviderCryptoworks(const unsigned char *a);
+ virtual bool MatchID(const unsigned char *data);
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual unsigned long long ProvId(void);
+ };
+
+class cCardCryptoworks : public cCard {
+private:
+ unsigned char *sharedEmm;
+ int sharedLen, globalToggle;
+ unsigned int globalCrc;
+public:
+ unsigned char addr[5];
+ //
+ cCardCryptoworks(const unsigned char *a);
+ virtual ~cCardCryptoworks();
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual int UpdateType(const unsigned char *data);
+ virtual int Assemble(cAssembleData *ad);
+ };
+
+// -- NDS -----------------------------------------------------------
+
+class cProviderNDS : public cProvider {
+public:
+ unsigned char sa[4];
+ //
+ cProviderNDS(const unsigned char *s);
+ virtual bool MatchID(const unsigned char *data);
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual unsigned long long ProvId(void);
+ virtual int Assemble(cAssembleData *ad);
+ };
+
+class cCardNDS : public cCard {
+public:
+ unsigned char ua[4];
+ //
+ cCardNDS(const unsigned char *u);
+ virtual bool MatchEMM(const unsigned char *data);
+ virtual int Assemble(cAssembleData *ad);
+ };
+
+class cParseNDS {
+public:
+ static unsigned int NumAddr(const unsigned char *data);
+ static int AddrMode(const unsigned char *data);
+ static bool IsSA(const unsigned char *data);
+ static bool HasAddr(const unsigned char *data, const unsigned char *a);
+ static const unsigned char *PayloadStart(const unsigned char *data);
+ static int PayloadSize(const unsigned char *data);
+ static int Assemble(cAssembleData *ad, const unsigned char *a);
+ };
+
+#endif //___PARSE_H
--- /dev/null
+diff -ur linux/drivers/media/dvb/ttpci/av7110_ca.c linux/drivers/media/dvb/ttpci/av7110_ca.c
+--- linux/drivers/media/dvb/ttpci/av7110_ca.c 2004-01-09 14:44:57.000000000 +0100
++++ linux/drivers/media/dvb/ttpci/av7110_ca.c 2005-09-02 16:52:08.000000000 +0200
+@@ -263,10 +263,31 @@
+ }
+
+ case CA_GET_MSG:
+- break;
++ {
++ ca_pid_t *arg = (ca_pid_t*) parg;
++ u16 buf[4], res[2];
++ buf[0]=0x0745;
++ buf[1]=2;
++ buf[2]=arg->pid >> 16;
++ buf[3]=arg->pid & 0xFFFF;
++ av7110_fw_request(av7110,buf,sizeof(buf),res,2);
++ arg->index=(res[0]<<16) + res[1];
++ break;
++ }
+
+ case CA_SEND_MSG:
+- break;
++ {
++ ca_pid_t *arg = (ca_pid_t*) parg;
++ u16 buf[6], res[2];
++ buf[0]=0x0746;
++ buf[1]=4;
++ buf[2]=arg->pid >> 16;
++ buf[3]=arg->pid & 0xFFFF;
++ buf[4]=arg->index >> 16;
++ buf[5]=arg->index & 0xFFFF;
++ av7110_fw_request(av7110,buf,sizeof(buf),res,2);
++ break;
++ }
+
+ case CA_GET_DESCR_INFO:
+ {
+@@ -295,6 +316,37 @@
+ break;
+ }
+
++ case CA_SET_PID:
++ {
++ int handle;
++ ca_pid_t *pid = (ca_pid_t*) parg;
++
++ if (pid->pid >= 0x1fff)
++ return -EINVAL;
++ if (pid->index < 0 || pid->index >= 16)
++ return -EINVAL;
++
++ if (down_interruptible (&av7110->demux.mutex))
++ return -ERESTARTSYS;
++
++ for(handle=0; handle<32; handle++) {
++ struct dvb_demux_filter *dvbdmxfilter=av7110->handle2filter[handle];
++ if(dvbdmxfilter) {
++ struct dvb_demux_feed *feed=dvbdmxfilter->feed;
++ if(feed && feed->state==DMX_STATE_GO && feed->pid==pid->pid) {
++ /* we map the new cmd to CacheError as it's not
++ implemented anyways, i.e. free. */
++ av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, CacheError, 1,
++ (handle<<8)|pid->index); /* hw handle / cw index*/
++ break;
++ }
++ }
++ }
++
++ up(&av7110->demux.mutex);
++ break;
++ }
++
+ default:
+ return -EINVAL;
+ }
--- /dev/null
+diff -ur linux/drivers/media/dvb/ttpci/av7110_ca.c linux/drivers/media/dvb/ttpci/av7110_ca.c
+--- linux/drivers/media/dvb/ttpci/av7110_ca.c 2004-01-09 14:44:57.000000000 +0100
++++ linux/drivers/media/dvb/ttpci/av7110_ca.c 2006-05-09 19:49:42.000000000 +0200
+@@ -37,6 +37,9 @@
+ #include <linux/poll.h>
+ #include <linux/byteorder/swabb.h>
+ #include <linux/smp_lock.h>
++#ifndef LINUX_VERSION_CODE
++#include <linux/version.h>
++#endif
+
+ #define DEBUG_VARIABLE av7110_debug
+ extern int av7110_debug;
+@@ -263,10 +266,31 @@
+ }
+
+ case CA_GET_MSG:
+- break;
++ {
++ ca_pid_t *arg = (ca_pid_t*) parg;
++ u16 buf[4], res[2];
++ buf[0]=0x0745;
++ buf[1]=2;
++ buf[2]=arg->pid >> 16;
++ buf[3]=arg->pid & 0xFFFF;
++ av7110_fw_request(av7110,buf,sizeof(buf),res,2);
++ arg->index=(res[0]<<16) + res[1];
++ break;
++ }
+
+ case CA_SEND_MSG:
+- break;
++ {
++ ca_pid_t *arg = (ca_pid_t*) parg;
++ u16 buf[6], res[2];
++ buf[0]=0x0746;
++ buf[1]=4;
++ buf[2]=arg->pid >> 16;
++ buf[3]=arg->pid & 0xFFFF;
++ buf[4]=arg->index >> 16;
++ buf[5]=arg->index & 0xFFFF;
++ av7110_fw_request(av7110,buf,sizeof(buf),res,2);
++ break;
++ }
+
+ case CA_GET_DESCR_INFO:
+ {
+@@ -295,6 +319,45 @@
+ break;
+ }
+
++ case CA_SET_PID:
++ {
++ int handle;
++ ca_pid_t *pid = (ca_pid_t*) parg;
++
++ if (pid->pid >= 0x1fff)
++ return -EINVAL;
++ if (pid->index < 0 || pid->index >= 16)
++ return -EINVAL;
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16))
++ if (down_interruptible (&av7110->demux.mutex))
++#else
++ if (mutex_lock_interruptible (&av7110->demux.mutex))
++#endif
++ return -ERESTARTSYS;
++
++ for(handle=0; handle<32; handle++) {
++ struct dvb_demux_filter *dvbdmxfilter=av7110->handle2filter[handle];
++ if(dvbdmxfilter) {
++ struct dvb_demux_feed *feed=dvbdmxfilter->feed;
++ if(feed && feed->state==DMX_STATE_GO && feed->pid==pid->pid) {
++ /* we map the new cmd to CacheError as it's not
++ implemented anyways, i.e. free. */
++ av7110_fw_cmd(av7110, COMTYPE_PIDFILTER, CacheError, 1,
++ (handle<<8)|pid->index); /* hw handle / cw index*/
++ break;
++ }
++ }
++ }
++
++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16))
++ up(&av7110->demux.mutex);
++#else
++ mutex_unlock(&av7110->demux.mutex);
++#endif
++ break;
++ }
++
+ default:
+ return -EINVAL;
+ }
--- /dev/null
+--- linux/drivers/media/dvb/dvb-core/dvb_demux.c 2004-03-03 17:49:42.000000000 +0100
++++ linux/drivers/media/dvb/dvb-core/dvb_demux.c 2004-04-05 12:44:59.000000000 +0200
+@@ -329,8 +329,8 @@
+ ** in the following dvb_dmx_swfilter_section_new
+ */
+ #endif
+- dvb_dmx_swfilter_section_new(feed);
+- return 0;
++ //dvb_dmx_swfilter_section_new(feed);
++ //return 0;
+ }
+
+ if(buf[1] & 0x40)
--- /dev/null
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/ci.c vdr-1.4.7-sc7/ci.c
+--- vdr-1.4.7-orig/ci.c 2007-04-30 14:58:41.000000000 +0200
++++ vdr-1.4.7-sc7/ci.c 2007-05-13 18:04:04.000000000 +0200
+@@ -1502,9 +1502,8 @@
+ close(fd);
+ }
+
+-cCiHandler *cCiHandler::CreateCiHandler(const char *FileName)
++cCiHandler *cCiHandler::CreateCiHandler(int fd_ca)
+ {
+- int fd_ca = open(FileName, O_RDWR);
+ if (fd_ca >= 0) {
+ ca_caps_t Caps;
+ if (ioctl(fd_ca, CA_GET_CAP, &Caps) == 0) {
+@@ -1520,8 +1519,7 @@
+ esyslog("ERROR: no CAM slots found");
+ }
+ else
+- LOG_ERROR_STR(FileName);
+- close(fd_ca);
++ LOG_ERROR_STR("CA_GET_CAP");
+ }
+ return NULL;
+ }
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/ci.h vdr-1.4.7-sc7/ci.h
+--- vdr-1.4.7-orig/ci.h 2006-08-12 11:43:31.000000000 +0200
++++ vdr-1.4.7-sc7/ci.h 2007-05-13 18:41:42.000000000 +0200
+@@ -85,10 +85,12 @@
+ class cCiCaProgramData : public cListObject {
+ public:
+ int programNumber;
++ bool modified;
+ cList<cCiCaPidData> pidList;
+ cCiCaProgramData(int ProgramNumber)
+ {
+ programNumber = ProgramNumber;
++ modified = true;
+ }
+ };
+
+@@ -96,6 +98,8 @@
+ class cCiTransportLayer;
+ class cCiTransportConnection;
+
++#define VDR_IS_SC_PATCHED 401
++
+ class cCiHandler {
+ private:
+ cMutex mutex;
+@@ -123,7 +127,7 @@
+ void SendCaPmt(void);
+ public:
+ ~cCiHandler();
+- static cCiHandler *CreateCiHandler(const char *FileName);
++ static cCiHandler *CreateCiHandler(int fd_ca);
+ ///< Creates a new cCiHandler for the given CA device.
+ int NumSlots(void) { return numSlots; }
+ ///< Returns the number of CAM slots provided by this CA device.
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/device.c vdr-1.4.7-sc7/device.c
+--- vdr-1.4.7-orig/device.c 2006-09-03 12:13:25.000000000 +0200
++++ vdr-1.4.7-sc7/device.c 2007-05-13 18:30:57.000000000 +0200
+@@ -395,6 +395,54 @@
+ return false;
+ }
+
++void cDevice::CiStartDecrypting(void)
++{
++ if (ciHandler)
++ ciHandler->StartDecrypting();
++}
++
++void cDevice::CiSetSource(int Source, int Transponder)
++{
++ cMutexLock MutexLock(&ciListMutex);
++ if (ciSource != Source || ciTransponder != Transponder)
++ ciProgramList.Clear();
++ ciSource = Source;
++ ciTransponder = Transponder;
++}
++
++void cDevice::CiAddPid(int ProgramNumber, int Pid, int StreamType)
++{
++ cMutexLock MutexLock(&ciListMutex);
++ cCiCaProgramData *ProgramData = NULL;
++ for (cCiCaProgramData *p = ciProgramList.First(); p; p = ciProgramList.Next(p)) {
++ if (p->programNumber == ProgramNumber) {
++ ProgramData = p;
++ for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
++ if (q->pid == Pid)
++ return;
++ }
++ }
++ }
++ if (!ProgramData)
++ ciProgramList.Add(ProgramData = new cCiCaProgramData(ProgramNumber));
++ ProgramData->pidList.Add(new cCiCaPidData(Pid, StreamType));
++ ProgramData->modified=true;
++}
++
++void cDevice::CiSetPid(int Pid, bool Active)
++{
++ cMutexLock MutexLock(&ciListMutex);
++ for (cCiCaProgramData *p = ciProgramList.First(); p; p = ciProgramList.Next(p)) {
++ for (cCiCaPidData *q = p->pidList.First(); q; q = p->pidList.Next(q)) {
++ if (q->pid == Pid) {
++ q->active = Active;
++ p->modified = true;
++ return;
++ }
++ }
++ }
++}
++
+ bool cDevice::AddPid(int Pid, ePidType PidType)
+ {
+ if (Pid || PidType == ptPcr) {
+@@ -424,6 +472,7 @@
+ }
+ if (ciHandler)
+ ciHandler->SetPid(Pid, true);
++ CiSetPid(Pid, true);
+ }
+ PRINTPIDS("a");
+ return true;
+@@ -453,6 +502,7 @@
+ }
+ if (ciHandler)
+ ciHandler->SetPid(Pid, true);
++ CiSetPid(Pid, true);
+ }
+ }
+ return true;
+@@ -481,6 +531,7 @@
+ pidHandles[n].pid = 0;
+ if (ciHandler)
+ ciHandler->SetPid(Pid, false);
++ CiSetPid(Pid, false);
+ }
+ }
+ PRINTPIDS("E");
+@@ -663,6 +714,16 @@
+ }
+ #endif
+ }
++
++ CiSetSource(Channel->Source(), Channel->Transponder());
++ if (Channel->Ca() >= CA_ENCRYPTED_MIN) {
++ CiAddPid(Channel->Sid(), Channel->Vpid(), 2);
++ for (const int *Apid = Channel->Apids(); *Apid; Apid++)
++ CiAddPid(Channel->Sid(), *Apid, 4);
++ for (const int *Dpid = Channel->Dpids(); *Dpid; Dpid++)
++ CiAddPid(Channel->Sid(), *Dpid, 0);
++ }
++
+ if (NeedsDetachReceivers)
+ DetachAllReceivers();
+ if (SetChannelDevice(Channel, LiveView)) {
+@@ -672,8 +733,7 @@
+ sectionHandler->SetStatus(true);
+ }
+ // Start decrypting any PIDs that might have been set in SetChannelDevice():
+- if (ciHandler)
+- ciHandler->StartDecrypting();
++ CiStartDecrypting();
+ }
+ else
+ Result = scrFailed;
+@@ -1258,8 +1318,7 @@
+ Unlock();
+ if (!Running())
+ Start();
+- if (ciHandler)
+- ciHandler->StartDecrypting();
++ CiStartDecrypting();
+ return true;
+ }
+ }
+@@ -1286,8 +1345,7 @@
+ else if (receiver[i])
+ receiversLeft = true;
+ }
+- if (ciHandler)
+- ciHandler->StartDecrypting();
++ CiStartDecrypting();
+ if (!receiversLeft)
+ Cancel(3);
+ }
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/device.h vdr-1.4.7-sc7/device.h
+--- vdr-1.4.7-orig/device.h 2006-06-15 11:32:48.000000000 +0200
++++ vdr-1.4.7-sc7/device.h 2007-05-13 18:04:04.000000000 +0200
+@@ -311,6 +311,14 @@
+
+ protected:
+ cCiHandler *ciHandler;
++ int ciSource, ciTransponder;
++ cList<cCiCaProgramData> ciProgramList;
++ cMutex ciListMutex;
++ virtual void CiStartDecrypting(void);
++ virtual bool CiAllowConcurrent(void) const { return false; }
++ void CiSetSource(int Source, int Transponder);
++ void CiAddPid(int ProgramNumber, int Pid, int StreamType);
++ void CiSetPid(int Pid, bool Active);
+ public:
+ cCiHandler *CiHandler(void) { return ciHandler; }
+
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/dvbdevice.c vdr-1.4.7-sc7/dvbdevice.c
+--- vdr-1.4.7-orig/dvbdevice.c 2007-02-24 12:10:14.000000000 +0100
++++ vdr-1.4.7-sc7/dvbdevice.c 2007-05-13 18:04:04.000000000 +0200
+@@ -419,7 +419,11 @@
+ dvb_frontend_info feinfo;
+ if (ioctl(fd_frontend, FE_GET_INFO, &feinfo) >= 0) {
+ frontendType = feinfo.type;
+- ciHandler = cCiHandler::CreateCiHandler(*cDvbName(DEV_DVB_CA, n));
++ int fd_ca = DvbOpen(DEV_DVB_CA, n, O_RDWR);
++ if(fd_ca>=0) {
++ ciHandler = cCiHandler::CreateCiHandler(fd_ca);
++ if(!ciHandler) close(fd_ca);
++ }
+ dvbTuner = new cDvbTuner(fd_frontend, CardIndex(), frontendType, ciHandler);
+ }
+ else
+@@ -776,8 +780,12 @@
+ if (Channel->Vpid() && !HasPid(Channel->Vpid()) || Channel->Apid(0) && !HasPid(Channel->Apid(0))) {
+ #ifdef DO_MULTIPLE_RECORDINGS
+ #ifndef DO_MULTIPLE_CA_CHANNELS
+- if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN)
+- needsDetachReceivers = Ca() != Channel->Ca();
++ if (Ca() >= CA_ENCRYPTED_MIN || Channel->Ca() >= CA_ENCRYPTED_MIN) {
++ if(Channel->Ca()<CA_ENCRYPTED_MIN || CiAllowConcurrent())
++ return true;
++ else
++ needsDetachReceivers = Ca() != Channel->Ca();
++ }
+ else
+ #endif
+ if (!IsPrimaryDevice())
+@@ -927,7 +935,7 @@
+ SetPid(&pidHandles[ptAudio], ptAudio, true);
+ if (ciHandler) {
+ ciHandler->SetPid(pidHandles[ptAudio].pid, true);
+- ciHandler->StartDecrypting();
++ CiStartDecrypting();
+ }
+ }
+ }
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/tools.c vdr-1.4.7-sc7/tools.c
+--- vdr-1.4.7-orig/tools.c 2006-12-02 12:12:59.000000000 +0100
++++ vdr-1.4.7-sc7/tools.c 2007-05-13 18:04:04.000000000 +0200
+@@ -542,9 +542,9 @@
+
+ // --- cTimeMs ---------------------------------------------------------------
+
+-cTimeMs::cTimeMs(void)
++cTimeMs::cTimeMs(int Ms)
+ {
+- Set();
++ Set(Ms);
+ }
+
+ uint64_t cTimeMs::Now(void)
+diff -urN -X ex.vdr-sc7 vdr-1.4.7-orig/tools.h vdr-1.4.7-sc7/tools.h
+--- vdr-1.4.7-orig/tools.h 2006-12-03 18:38:38.000000000 +0100
++++ vdr-1.4.7-sc7/tools.h 2007-05-13 18:04:04.000000000 +0200
+@@ -162,7 +162,7 @@
+ private:
+ uint64_t begin;
+ public:
+- cTimeMs(void);
++ cTimeMs(int Ms = 0);
+ static uint64_t Now(void);
+ void Set(int Ms = 0);
+ bool TimedOut(void);
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <malloc.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <typeinfo>
+#ifndef STATICBUILD
+#include <dlfcn.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#endif
+
+#include <vdr/plugin.h>
+#include <vdr/menuitems.h>
+#include <vdr/status.h>
+#include <vdr/dvbdevice.h>
+#include <vdr/channels.h>
+#include <vdr/ci.h>
+#include <vdr/interface.h>
+#include <vdr/menu.h>
+#include <vdr/tools.h>
+#include <vdr/config.h>
+
+#include "sc.h"
+#include "scsetup.h"
+#include "filter.h"
+#include "system.h"
+#include "cam.h"
+#include "smartcard.h"
+#include "data.h"
+#include "network.h"
+#include "misc.h"
+#include "opts.h"
+#include "i18n.h"
+#include "log-core.h"
+#include "version.h"
+
+#define MIN_VERS 1 // required VDR version
+#define MIN_MAJOR 4
+#define MIN_MINOR 6
+#define MINAPIVERSNUM 10405
+
+// some sanity checks
+#ifdef HAVE_SOFTCSA
+#error softcsa/ffdecsa patch MUST NOT be applied. Next time read the README first.
+#endif
+#if APIVERSNUM >= 10500
+#ifdef VDR_IS_SC_PATCHED
+#error You MUST NOT patch the VDR core. Next time read the README first.
+#endif
+#else //APIVERSNUM >= 10500
+#if !defined(VDR_IS_SC_PATCHED)
+#error You MUST patch the VDR core with the supplied patch. Next time read the README first.
+#endif
+#if VDR_IS_SC_PATCHED<401
+#error Your VDR core is patched with an outdated patch version. Please upgrade to the supplied version.
+#endif
+#endif //APIVERSNUM >= 10500
+#if APIVERSNUM<MINAPIVERSNUM
+#error Your VDR API version is too old. See README.
+#endif
+
+// SC API version number for loading shared libraries
+#define SCAPIVERS 8
+
+const char *ScVersion = SCVERSION;
+
+static cPlugin *ScPlugin;
+static cOpts *ScOpts, *LogOpts;
+
+static const struct LogModule lm_core = {
+ (LMOD_ENABLE|L_CORE_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_CORE_LOAD|L_CORE_ECM|L_CORE_PIDS|L_CORE_AU|L_CORE_AUSTATS|L_CORE_CAIDS|L_CORE_NET|L_CORE_CI|L_CORE_SC|L_CORE_HOOK)&LOPT_MASK,
+ "core",
+ { "load","action","ecm","ecmProc","pids","au","auStats","auExtra","auExtern",
+ "caids","keys","dynamic","csa","ci","av7110","net","netData","msgcache",
+ "serial","smartcard","hook","ciFull" }
+ };
+ADD_MODULE(L_CORE,lm_core)
+
+// --- cMenuEditCapItem --------------------------------------------------------
+
+class cMenuEditCapItem : public cMenuEditIntItem {
+protected:
+ virtual void Set(void);
+public:
+ cMenuEditCapItem(const char *Name, int *Value);
+ eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuEditCapItem::cMenuEditCapItem(const char *Name, int *Value)
+:cMenuEditIntItem(Name, Value, 0)
+{
+ Set();
+}
+
+void cMenuEditCapItem::Set(void)
+{
+ if(!*value) SetValue(tr("off"));
+ else cMenuEditIntItem::Set();
+}
+
+eOSState cMenuEditCapItem::ProcessKey(eKeys Key)
+{
+ eOSState state = cMenuEditItem::ProcessKey(Key);
+ if(state==osUnknown)
+ state=cMenuEditIntItem::ProcessKey(Key);
+ return state;
+}
+
+// --- cMenuEditHexItem ------------------------------------------------------
+
+class cMenuEditHexItem : public cMenuEditItem {
+private:
+ bool abc, isOn;
+ //
+ void SetButtons(bool on);
+protected:
+ int *value;
+ int min, max;
+ //
+ virtual void Set(void);
+public:
+ cMenuEditHexItem(const char *Name, int *Value, int Min=0, int Max=INT_MAX);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuEditHexItem::cMenuEditHexItem(const char *Name, int *Value, int Min, int Max)
+:cMenuEditItem(Name)
+{
+ value=Value; min=Min; max=Max;
+ if(*value<min) *value=min;
+ else if(*value>max) *value=max;
+ Set();
+ abc=true; isOn=false;
+}
+
+void cMenuEditHexItem::SetButtons(bool on)
+{
+ if(on) {
+ if(abc) cSkinDisplay::Current()->SetButtons("A","B","C","D-F");
+ else cSkinDisplay::Current()->SetButtons("D","E","F","A-C");
+ isOn=true;
+ }
+ else {
+ cSkinDisplay::Current()->SetButtons(0);
+ isOn=false;
+ }
+}
+
+void cMenuEditHexItem::Set(void)
+{
+ char buf[16];
+ snprintf(buf,sizeof(buf),"%X",*value);
+ SetValue(buf);
+}
+
+eOSState cMenuEditHexItem::ProcessKey(eKeys Key)
+{
+ switch(NORMALKEY(Key)) {
+ case kUp:
+ case kDown:
+ if(isOn) SetButtons(false);
+ break;
+ default:
+ if(!isOn) SetButtons(true);
+ break;
+ }
+ eOSState state=cMenuEditItem::ProcessKey(Key);
+ if(state!=osUnknown) return state;
+
+ int newValue=*value;
+ bool IsRepeat=Key & k_Repeat;
+ Key=NORMALKEY(Key);
+ switch(Key) {
+ case kBlue:
+ abc=!abc; SetButtons(true);
+ break;
+ case kRed:
+ case kGreen:
+ case kYellow:
+ case k0 ... k9:
+ {
+ if(fresh) { newValue=0; fresh=false; }
+ int add;
+ if(Key>=kRed && Key<=kYellow) add=(abc ? 10:13)+(Key-kRed);
+ else add=(Key-k0);
+ newValue=newValue*16+add;
+ break;
+ }
+ case kLeft:
+ newValue=*value-1; fresh=true;
+ if(!IsRepeat && newValue<min) newValue=max;
+ break;
+ case kRight:
+ newValue=*value+1; fresh=true;
+ if(!IsRepeat && newValue>max) newValue=min;
+ break;
+ default:
+ if(*value<min) { *value=min; Set(); }
+ if(*value>max) { *value=max; Set(); }
+ return osUnknown;
+ }
+ if(newValue!=*value && (!fresh || min<=newValue) && newValue<=max) {
+ *value=newValue;
+ Set();
+ }
+ return osContinue;
+}
+
+// --- cScInfoItem -------------------------------------------------------------
+
+class cScInfoItem : public cOsdItem {
+private:
+ void SetValue(const char *Name, const char *Value);
+public:
+ cScInfoItem(const char *Name, int Value, eOSState State=osUnknown);
+ cScInfoItem(const char *Name, const char *Value=0, eOSState State=osUnknown);
+ };
+
+cScInfoItem::cScInfoItem(const char *Name, int Value, eOSState State)
+:cOsdItem(State)
+{
+ char buf[16];
+ snprintf(buf,sizeof(buf),"%d",Value);
+ SetValue(Name,buf);
+ if(State==osUnknown) SetSelectable(false);
+}
+
+cScInfoItem::cScInfoItem(const char *Name, const char *Value, eOSState State)
+:cOsdItem(State)
+{
+ SetValue(Name,Value);
+ if(State==osUnknown) SetSelectable(false);
+}
+
+void cScInfoItem::SetValue(const char *Name, const char *Value)
+{
+ char *buff;
+ asprintf(&buff,Value ? "%s:\t%s":"%s",Name,Value);
+ SetText(buff,false);
+ cStatus::MsgOsdCurrentItem(buff);
+}
+
+// --- cOpt --------------------------------------------------------------------
+
+cOpt::cOpt(const char *Name, const char *Title)
+{
+ name=Name; title=Title;
+ fullname=0; persistant=true;
+}
+
+cOpt::~cOpt()
+{
+ free(fullname);
+}
+
+const char *cOpt::FullName(const char *PreStr)
+{
+ if(PreStr) {
+ free(fullname);
+ asprintf(&fullname,"%s.%s",PreStr,name);
+ return fullname;
+ }
+ else return name;
+}
+
+// --- cOptInt -----------------------------------------------------------------
+
+cOptInt::cOptInt(const char *Name, const char *Title, int *Storage, int Min, int Max)
+:cOpt(Name,Title)
+{
+ storage=Storage; min=Min; max=Max;
+}
+
+void cOptInt::Parse(const char *Value)
+{
+ *storage=atoi(Value);
+}
+
+void cOptInt::Backup(void)
+{
+ value=*storage;
+}
+
+bool cOptInt::Set(void)
+{
+ if(value!=*storage) { *storage=value; return true; }
+ return false;
+}
+
+void cOptInt::Store(const char *PreStr)
+{
+ ScPlugin->SetupStore(FullName(PreStr),*storage);
+}
+
+void cOptInt::Create(cOsdMenu *menu)
+{
+ menu->Add(new cMenuEditIntItem(tr(title),&value,min,max));
+}
+
+// --- cOptSel -----------------------------------------------------------------
+
+cOptSel::cOptSel(const char *Name, const char *Title, int *Storage, int NumStr, const char * const *Strings)
+:cOptInt(Name,Title,Storage,0,NumStr)
+{
+ strings=Strings;
+ trStrings=0;
+}
+
+cOptSel::~cOptSel()
+{
+ free(trStrings);
+}
+
+void cOptSel::Create(cOsdMenu *menu)
+{
+ free(trStrings);
+ if((trStrings=MALLOC(const char *,max))) {
+ for(int i=0; i<max ; i++) trStrings[i]=tr(strings[i]);
+ menu->Add(new cMenuEditStraItem(tr(title),&value,max,trStrings));
+ }
+}
+
+// --- cOptBool -----------------------------------------------------------------
+
+cOptBool::cOptBool(const char *Name, const char *Title, int *Storage)
+:cOptInt(Name,Title,Storage,0,1)
+{}
+
+void cOptBool::Create(cOsdMenu *menu)
+{
+ menu->Add(new cMenuEditBoolItem(tr(title),&value));
+}
+
+// --- cOptStr -----------------------------------------------------------------
+
+cOptStr::cOptStr(const char *Name, const char *Title, char *Storage, int Size, const char *Allowed)
+:cOpt(Name,Title)
+{
+ storage=Storage; size=Size; allowed=Allowed;
+ value=MALLOC(char,size);
+}
+
+cOptStr::~cOptStr()
+{
+ free(value);
+}
+
+void cOptStr::Parse(const char *Value)
+{
+ strn0cpy(storage,Value,size);
+}
+
+void cOptStr::Backup(void)
+{
+ strn0cpy(value,storage,size);
+}
+
+bool cOptStr::Set(void)
+{
+ if(strcmp(value,storage)) { strn0cpy(storage,value,size); return true; }
+ return false;
+}
+
+void cOptStr::Store(const char *PreStr)
+{
+ ScPlugin->SetupStore(FullName(PreStr),storage);
+}
+
+void cOptStr::Create(cOsdMenu *menu)
+{
+ menu->Add(new cMenuEditStrItem(tr(title),value,size,allowed));
+}
+
+// --- cOptMInt ----------------------------------------------------------------
+
+class cOptMInt : public cOpt {
+protected:
+ int *storage, *value;
+ int size, mode, len;
+public:
+ cOptMInt(const char *Name, const char *Title, int *Storage, int Size, int Mode);
+ virtual ~cOptMInt();
+ virtual void Parse(const char *Value);
+ virtual void Backup(void);
+ virtual bool Set(void);
+ virtual void Store(const char *PreStr);
+ virtual void Create(cOsdMenu *menu);
+ };
+
+// mode: 0-Cap 1-Int 2-Hex
+
+cOptMInt::cOptMInt(const char *Name, const char *Title, int *Storage, int Size, int Mode)
+:cOpt(Name,Title)
+{
+ storage=Storage; size=Size; mode=Mode; len=sizeof(int)*size;
+ value=MALLOC(int,size);
+}
+
+cOptMInt::~cOptMInt()
+{
+ free(value);
+}
+
+void cOptMInt::Parse(const char *Value)
+{
+ memset(storage,0,len);
+ int i=0;
+ while(1) {
+ char *p;
+ const int c=strtol(Value,&p,mode>1 ? 16:10);
+ if(p==Value || i>=size) return;
+ if(c>0) storage[i++]=c;
+ Value=p;
+ }
+}
+
+void cOptMInt::Backup(void)
+{
+ memcpy(value,storage,len);
+}
+
+bool cOptMInt::Set(void)
+{
+ if(memcmp(value,storage,len)) {
+ memset(storage,0,len);
+ for(int i=0, k=0; i<size; i++) if(value[i]>0) storage[k++]=value[i];
+ return true;
+ }
+ return false;
+}
+
+void cOptMInt::Store(const char *PreStr)
+{
+ char b[256];
+ int p=0;
+ for(int i=0; i<size; i++)
+ if(storage[i]) p+=snprintf(b+p,sizeof(b)-p,mode>1 ? "%x ":"%d ",storage[i]);
+ ScPlugin->SetupStore(FullName(PreStr),p>0?b:0);
+}
+
+void cOptMInt::Create(cOsdMenu *menu)
+{
+ for(int i=0; i<size; i++) {
+ const char *buff=tr(title);
+ switch(mode) {
+ case 0: menu->Add(new cMenuEditCapItem(buff,&value[i])); break;
+ case 1: menu->Add(new cMenuEditIntItem(buff,&value[i],0,65535)); break;
+ case 2: menu->Add(new cMenuEditHexItem(buff,&value[i],0,65535)); break;
+ }
+ if(value[i]==0) break;
+ }
+}
+
+// --- cOpts -------------------------------------------------------------------
+
+cOpts::cOpts(const char *PreStr, int NumOpts)
+{
+ preStr=PreStr;
+ numOpts=NumOpts; numAdd=0;
+ if((opts=MALLOC(cOpt *,numOpts))) memset(opts,0,sizeof(cOpt *)*numOpts);
+}
+
+cOpts::~cOpts()
+{
+ if(opts) {
+ for(int i=0; i<numOpts; i++) delete opts[i];
+ free(opts);
+ }
+}
+
+void cOpts::Add(cOpt *opt)
+{
+ if(opts && numAdd<numOpts) opts[numAdd++]=opt;
+}
+
+bool cOpts::Parse(const char *Name, const char *Value)
+{
+ if(opts) {
+ for(int i=0; i<numAdd; i++)
+ if(opts[i] && opts[i]->Persistant() && !strcasecmp(Name,opts[i]->Name())) {
+ opts[i]->Parse(Value);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool cOpts::Store(bool AsIs)
+{
+ bool res=false;
+ if(opts) {
+ for(int i=0; i<numAdd; i++)
+ if(opts[i]) {
+ if(!AsIs && opts[i]->Set()) res=true;
+ if(opts[i]->Persistant()) opts[i]->Store(preStr);
+ }
+ }
+ return res;
+}
+
+void cOpts::Backup(void)
+{
+ if(opts) {
+ for(int i=0; i<numAdd; i++)
+ if(opts[i]) opts[i]->Backup();
+ }
+}
+
+void cOpts::Create(cOsdMenu *menu)
+{
+ if(opts) {
+ for(int i=0; i<numAdd; i++)
+ if(opts[i]) opts[i]->Create(menu);
+ }
+}
+
+// --- cMenuInfoSc -------------------------------------------------------------
+
+class cMenuInfoSc : public cOsdMenu {
+public:
+ cMenuInfoSc(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuInfoSc::cMenuInfoSc(void)
+:cOsdMenu(tr("SoftCAM"),25)
+{
+ Add(new cScInfoItem(tr("Current keys:")));
+ for(int d=0; d<MAXDVBDEVICES; d++) {
+ int n=0;
+ char *ks;
+ do {
+ if((ks=cSoftCAM::CurrKeyStr(d,n))) {
+ char buffer[32];
+ snprintf(buffer,sizeof(buffer)," [%s %d:%d]","DVB",d+1,n+1);
+ Add(new cScInfoItem(buffer,ks));
+ free(ks);
+ }
+ n++;
+ } while(ks);
+ }
+ if(Feature.KeyFile()) {
+ Add(new cScInfoItem(tr("Key update status:")));
+ int fk, nk;
+ cSystem::KeyStats(fk,nk);
+ Add(new cScInfoItem(tr(" [Seen keys]"),fk));
+ Add(new cScInfoItem(tr(" [New keys]"), nk));
+ }
+ Display();
+}
+
+eOSState cMenuInfoSc::ProcessKey(eKeys Key)
+{
+ eOSState state=cOsdMenu::ProcessKey(Key);
+ if(state==osUnknown && Key==kOk) state=osBack;
+ return state;
+}
+
+// --- cMenuInfoCard -----------------------------------------------------------
+
+class cMenuInfoCard : public cMenuText {
+private:
+ int port;
+ char infoStr[1024];
+public:
+ cMenuInfoCard(int Port);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuInfoCard::cMenuInfoCard(int Port)
+:cMenuText(tr("Smartcard"),0,fontFix)
+{
+ port=Port;
+ smartcards.CardInfo(port,infoStr,sizeof(infoStr));
+ SetText(infoStr);
+ SetHelp(tr("Reset card"));
+ Display();
+}
+
+eOSState cMenuInfoCard::ProcessKey(eKeys Key)
+{
+ if(Key==kRed && Interface->Confirm(tr("Really reset card?"))) {
+ smartcards.CardReset(port);
+ return osEnd;
+ }
+ eOSState state=cMenuText::ProcessKey(Key);
+ if(state==osUnknown) state=osContinue;
+ return state;
+}
+
+// --- cLogOptItem -------------------------------------------------------------
+
+class cLogOptItem : public cMenuEditBoolItem {
+private:
+ int o;
+public:
+ cLogOptItem(const char *Name, int O, int *val);
+ int Option(void) { return o; }
+ };
+
+cLogOptItem::cLogOptItem(const char *Name, int O, int *val)
+:cMenuEditBoolItem(Name,val)
+{
+ o=O;
+}
+
+// --- cMenuLogMod -------------------------------------------------------------
+
+class cMenuLogMod : public cOsdMenu {
+private:
+ int m;
+ int v[LOPT_NUM], cfg[LOPT_NUM];
+ //
+ void Store(void);
+public:
+ cMenuLogMod(int M);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuLogMod::cMenuLogMod(int M)
+:cOsdMenu(tr("Module config"),33)
+{
+ m=M;
+ Add(new cOsdItem(tr("Reset module to default"),osUser9));
+ const char *name=cLogging::GetModuleName(LCLASS(m,0));
+ int o=cLogging::GetModuleOptions(LCLASS(m,0));
+ if(o>=0) {
+ for(int i=0; i<LOPT_NUM; i++) {
+ const char *opt;
+ if(i==0) opt="enable";
+ else opt=cLogging::GetOptionName(LCLASS(m,1<<i));
+ if(opt) {
+ char buff[64];
+ snprintf(buff,sizeof(buff),"%s.%s",name,opt);
+ cfg[i]=(o&(1<<i)) ? 1:0;
+ v[i]=1;
+ Add(new cLogOptItem(buff,i,&cfg[i]));
+ }
+ else v[i]=0;
+ }
+ }
+ Display();
+}
+
+void cMenuLogMod::Store(void)
+{
+ int o=0;
+ for(int i=0; i<LOPT_NUM; i++) if(v[i] && cfg[i]) o|=(1<<i);
+ cLogging::SetModuleOptions(LCLASS(m,o));
+ ScSetup.Store(false);
+}
+
+eOSState cMenuLogMod::ProcessKey(eKeys Key)
+{
+ eOSState state=cOsdMenu::ProcessKey(Key);
+ switch(state) {
+ case osUser9:
+ if(Interface->Confirm(tr("Really reset module to default?"))) {
+ cLogging::SetModuleDefault(LCLASS(m,0));
+ ScSetup.Store(false); state=osBack;
+ }
+ break;
+
+ case osContinue:
+ if(NORMALKEY(Key)==kLeft || NORMALKEY(Key)==kRight) {
+ cLogOptItem *item=dynamic_cast<cLogOptItem *>(Get(Current()));
+ if(item) {
+ int o=item->Option();
+ cLogging::SetModuleOption(LCLASS(m,1<<o),cfg[o]);
+ }
+ }
+ break;
+
+ case osUnknown:
+ if(Key==kOk) { Store(); state=osBack; }
+ break;
+
+ default:
+ break;
+ }
+ return state;
+}
+
+// --- cLogModItem -------------------------------------------------------------
+
+class cLogModItem : public cOsdItem {
+private:
+ int m;
+public:
+ cLogModItem(const char *Name, int M);
+ int Module(void) { return m; }
+ };
+
+cLogModItem::cLogModItem(const char *Name, int M)
+:cOsdItem(osUnknown)
+{
+ m=M;
+ char buf[64];
+ snprintf(buf,sizeof(buf),"%s '%s'...",tr("Module"),Name);
+ SetText(buf,true);
+}
+
+// --- cMenuLogSys -------------------------------------------------------------
+
+class cMenuLogSys : public cOsdMenu {
+private:
+ void Store(void);
+public:
+ cMenuLogSys(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuLogSys::cMenuLogSys(void)
+:cOsdMenu(tr("Message logging"),33)
+{
+ LogOpts->Backup(); LogOpts->Create(this);
+ Add(new cOsdItem(tr("Disable ALL modules"),osUser9));
+ Add(new cOsdItem(tr("Reset ALL modules to default"),osUser8));
+ for(int m=1; m<LMOD_MAX; m++) {
+ const char *name=cLogging::GetModuleName(LCLASS(m,0));
+ if(name)
+ Add(new cLogModItem(name,m));
+ }
+ Display();
+}
+
+void cMenuLogSys::Store(void)
+{
+ char *lf=strdup(logcfg.logFilename);
+ ScSetup.Store(false);
+ if(!lf || strcmp(lf,logcfg.logFilename))
+ cLogging::ReopenLogfile();
+ free(lf);
+}
+
+eOSState cMenuLogSys::ProcessKey(eKeys Key)
+{
+ eOSState state=cOsdMenu::ProcessKey(Key);
+ switch(state) {
+ case osUser9:
+ if(Interface->Confirm(tr("Really disable ALL modules?"))) {
+ for(int m=1; m<LMOD_MAX; m++)
+ cLogging::SetModuleOption(LCLASS(m,LMOD_ENABLE),false);
+ Store(); state=osBack;
+ }
+ break;
+
+ case osUser8:
+ if(Interface->Confirm(tr("Really reset ALL modules to default?"))) {
+ for(int m=1; m<LMOD_MAX; m++)
+ cLogging::SetModuleDefault(LCLASS(m,0));
+ Store(); state=osBack;
+ }
+ break;
+
+ case osUnknown:
+ if(Key==kOk) {
+ cLogModItem *item=dynamic_cast<cLogModItem *>(Get(Current()));
+ if(item) state=AddSubMenu(new cMenuLogMod(item->Module()));
+ else { Store(); state=osBack; }
+ }
+ break;
+
+ default:
+ break;
+ }
+ return state;
+}
+
+// --- cMenuSysOpts -------------------------------------------------------------
+
+class cMenuSysOpts : public cOsdMenu {
+public:
+ cMenuSysOpts(void);
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+cMenuSysOpts::cMenuSysOpts(void)
+:cOsdMenu(tr("Cryptsystem options"),33)
+{
+ for(cOpts *opts=0; (opts=cSystems::GetSystemOpts(opts==0));) {
+ opts->Backup();
+ opts->Create(this);
+ }
+ Display();
+}
+
+eOSState cMenuSysOpts::ProcessKey(eKeys Key)
+{
+ eOSState state=cOsdMenu::ProcessKey(Key);
+ switch(state) {
+ case osContinue:
+ if(NORMALKEY(Key)==kUp || NORMALKEY(Key)==kDown) {
+ cOsdItem *item=Get(Current());
+ if(item) item->ProcessKey(kNone);
+ }
+ break;
+
+ case osUnknown:
+ if(Key==kOk) { ScSetup.Store(false); state=osBack; }
+ break;
+
+ default:
+ break;
+ }
+ return state;
+}
+
+// --- cMenuSetupSc ------------------------------------------------------------
+
+class cMenuSetupSc : public cMenuSetupPage {
+private:
+ char *cfgdir;
+protected:
+ virtual void Store(void);
+public:
+ cMenuSetupSc(const char *CfgDir);
+ virtual ~cMenuSetupSc();
+ virtual eOSState ProcessKey(eKeys Key);
+ };
+
+static eOSState portStates[] = { osUser1,osUser2,osUser3,osUser4 };
+#if MAX_PORTS!=4
+#error Update portStates[]
+#endif
+
+cMenuSetupSc::cMenuSetupSc(const char *CfgDir)
+{
+ cfgdir=strdup(CfgDir);
+ SetSection(tr("SoftCAM"));
+
+ ScOpts->Backup(); LogOpts->Backup();
+ for(cOpts *opts=0; (opts=cSystems::GetSystemOpts(opts==0));) opts->Backup();
+
+ ScOpts->Create(this);
+ Add(new cOsdItem(tr("Cryptsystem options..."),osUser5));
+ Add(new cOsdItem(tr("Message logging..."),osUser6));
+ if(Feature.SmartCard()) {
+ char id[IDSTR_LEN];
+ for(int i=0; smartcards.ListCard(i,id,sizeof(id)); i++) {
+ char buff[32];
+ snprintf(buff,sizeof(buff),"%s %d",tr("Smartcard interface"),i);
+ if(id[0]) Add(new cScInfoItem(buff,id,portStates[i]));
+ else Add(new cScInfoItem(buff,tr("(empty)")));
+ }
+ }
+ Add(new cOsdItem(tr("Status information..."),osUser8));
+ Add(new cOsdItem(tr("Flush ECM cache"),osUser7));
+ Add(new cOsdItem(tr("Reload files"),osUser9));
+}
+
+cMenuSetupSc::~cMenuSetupSc()
+{
+ free(cfgdir);
+}
+
+void cMenuSetupSc::Store(void)
+{
+ ScSetup.Store(false);
+}
+
+eOSState cMenuSetupSc::ProcessKey(eKeys Key)
+{
+ eOSState state = cOsdMenu::ProcessKey(Key);
+ switch(state) {
+ case osUser1...osUser4:
+ if(Feature.SmartCard()) {
+ for(unsigned int i=0; i<sizeof(portStates)/sizeof(eOSState); i++)
+ if(portStates[i]==state) return(AddSubMenu(new cMenuInfoCard(i)));
+ }
+ state=osContinue;
+ break;
+
+ case osUser7:
+ state=osContinue;
+ if(Interface->Confirm(tr("Really flush ECM cache?"))) {
+ ecmcache.Flush();
+ cLoaders::SaveCache();
+ state=osEnd;
+ }
+ break;
+
+ case osUser8:
+ return AddSubMenu(new cMenuInfoSc);
+
+ case osUser6:
+ return AddSubMenu(new cMenuLogSys);
+
+ case osUser5:
+ return AddSubMenu(new cMenuSysOpts);
+
+ case osUser9:
+ state=osContinue;
+ if(!cSoftCAM::Active()) {
+ if(Interface->Confirm(tr("Really reload files?"))) {
+ Store();
+ cSoftCAM::Load(cfgdir);
+ state=osEnd;
+ }
+ }
+ else
+ Skins.Message(mtError,tr("Active! Can't reload files now"));
+ break;
+
+ case osContinue:
+ if(NORMALKEY(Key)==kUp || NORMALKEY(Key)==kDown) {
+ cOsdItem *item=Get(Current());
+ if(item) item->ProcessKey(kNone);
+ }
+ break;
+
+ case osUnknown:
+ if(Key==kOk) { Store(); state=osBack; }
+ break;
+
+ default:
+ break;
+ }
+ return state;
+}
+
+// --- cScSetup ---------------------------------------------------------------
+
+cScSetup ScSetup;
+
+cScSetup::cScSetup(void)
+{
+ AutoUpdate = 1;
+ memset(ScCaps,0,sizeof(ScCaps));
+ ScCaps[0] = 1;
+ ScCaps[1] = 2;
+ ConcurrentFF = 0;
+ memset(CaIgnore,0,sizeof(CaIgnore));
+ LocalPriority = 0;
+}
+
+void cScSetup::Check(void)
+{
+ if(AutoUpdate==0)
+ PRINTF(L_GEN_WARN,"Keys updates (AU) are disabled.");
+ for(int i=0; i<MAXSCCAPS; i++)
+ if(ScCaps[i]>=16) {
+ PRINTF(L_GEN_WARN,"ScCaps contains unusual value. Check your config! (You can ignore this message if you have more than 16 dvb cards in your system ;)");
+ break;
+ }
+
+ PRINTF(L_CORE_LOAD,"** Plugin config:");
+ PRINTF(L_CORE_LOAD,"** Key updates (AU) are %s",AutoUpdate?(AutoUpdate==1?"enabled (active CAIDs)":"enabled (all CAIDs)"):"DISABLED");
+ PRINTF(L_CORE_LOAD,"** Local systems %stake priority over cached remote",LocalPriority?"":"DON'T ");
+ PRINTF(L_CORE_LOAD,"** Concurrent FF recordings are %sallowed",ConcurrentFF?"":"NOT ");
+ LBSTART(L_CORE_LOAD);
+ LBPUT("** ScCaps are"); for(int i=0; i<MAXSCCAPS ; i++) LBPUT(" %d",ScCaps[i]);
+ LBFLUSH();
+ LBPUT("** Ignored CAIDs"); for(int i=0; i<MAXCAIGN ; i++) LBPUT(" %04X",CaIgnore[i]);
+ LBEND();
+}
+
+void cScSetup::Store(bool AsIs)
+{
+ if(ScOpts) ScOpts->Store(AsIs);
+ cSystems::ConfigStore(AsIs);
+ if(LogOpts) LogOpts->Store(AsIs);
+ cLineBuff lb(128);
+ if(cLogging::GetConfig(&lb))
+ ScPlugin->SetupStore("LogConfig",lb.Line());
+}
+
+bool cScSetup::CapCheck(int n)
+{
+ for(int j=0; j<MAXSCCAPS; j++)
+ if(ScCaps[j] && ScCaps[j]==n+1) return true;
+ return false;
+}
+
+bool cScSetup::Ignore(unsigned short caid)
+{
+ for(int i=0; i<MAXCAIGN; i++)
+ if(CaIgnore[i]==caid) return true;
+ return false;
+}
+
+// --- cSoftCAM ---------------------------------------------------------------
+
+bool cSoftCAM::Load(const char *cfgdir)
+{
+ ecmcache.Load();
+ if(Feature.KeyFile() && !keys.Load(cfgdir))
+ PRINTF(L_GEN_ERROR,"no keys loaded for softcam!");
+ if(!cSystems::Init(cfgdir)) return false;
+ if(Feature.SmartCard()) smartcards.LoadData(cfgdir);
+ cLoaders::LoadCache(cfgdir);
+ cLoaders::SaveCache();
+ return true;
+}
+
+void cSoftCAM::Shutdown(void)
+{
+ cSystems::Clean();
+ smartcards.Shutdown();
+ keys.Clear();
+}
+
+char *cSoftCAM::CurrKeyStr(int CardNum, int num)
+{
+ cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(CardNum));
+ char *str=0;
+ if(dev) {
+ if(dev->Cam()) str=dev->Cam()->CurrentKeyStr(num);
+ if(!str && num==0 && ScSetup.CapCheck(CardNum)) str=strdup(tr("(none)"));
+ }
+ return str;
+}
+
+bool cSoftCAM::Active(void)
+{
+ for(int n=cDevice::NumDevices(); --n>=0;) {
+ cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(n));
+ if(dev && dev->Cam() && dev->Cam()->Active()) return true;
+ }
+ return false;
+}
+
+void cSoftCAM::SetLogStatus(int CardNum, const cEcmInfo *ecm, bool on)
+{
+ cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(CardNum));
+ if(dev && dev->Cam()) dev->Cam()->LogEcmStatus(ecm,on);
+}
+
+void cSoftCAM::AddHook(int CardNum, cLogHook *hook)
+{
+ cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(CardNum));
+ if(dev && dev->Cam()) dev->Cam()->AddHook(hook);
+}
+
+bool cSoftCAM::TriggerHook(int CardNum, int id)
+{
+ cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(CardNum));
+ return dev && dev->Cam() && dev->Cam()->TriggerHook(id);
+}
+
+#ifndef STATICBUILD
+
+// --- cScDll ------------------------------------------------------------------
+
+class cScDll : public cSimpleItem {
+private:
+ char *fileName;
+ void *handle;
+public:
+ cScDll(const char *FileName);
+ ~cScDll();
+ bool Load(void);
+ };
+
+cScDll::cScDll(const char *FileName)
+{
+ fileName=strdup(FileName);
+ handle=0;
+}
+
+cScDll::~cScDll()
+{
+ if(handle) dlclose(handle);
+ free(fileName);
+}
+
+bool cScDll::Load(void)
+{
+ char *base=rindex(fileName,'/');
+ if(!base) base=fileName;
+ PRINTF(L_CORE_DYN,"loading library: %s",base);
+ if(!handle) {
+ handle=dlopen(fileName,RTLD_NOW|RTLD_LOCAL);
+ if(handle) return true;
+ PRINTF(L_GEN_ERROR,"dload: %s: %s",base,dlerror());
+ }
+ return false;
+}
+
+// --- cScDlls -----------------------------------------------------------------
+
+#define LIBSC_PREFIX "libsc-"
+#define SO_INDICATOR ".so."
+
+class cScDlls : public cSimpleList<cScDll> {
+private:
+ void *handle;
+public:
+ cScDlls(void);
+ ~cScDlls();
+ bool Load(void);
+ };
+
+cScDlls::cScDlls(void)
+{
+ handle=0;
+}
+
+cScDlls::~cScDlls()
+{
+ Clear();
+ if(handle) dlclose(handle);
+ PRINTF(L_CORE_DYN,"unload done");
+}
+
+bool cScDlls::Load(void)
+{
+ Dl_info info;
+ static int marker=0;
+ if(!dladdr((void *)&marker,&info)) {
+ PRINTF(L_GEN_ERROR,"dladdr: %s",dlerror());
+ return false;
+ }
+
+ // we have to re-dlopen our selfs as VDR doesn't use RTLD_GLOBAL
+ // but our symbols have to be available to the sub libs.
+ handle=dlopen(info.dli_fname,RTLD_NOW|RTLD_GLOBAL);
+ if(!handle) {
+ PRINTF(L_GEN_ERROR,"dlopen myself: %s",dlerror());
+ return false;
+ }
+
+ char *path=strdup(info.dli_fname);
+ char *p;
+ if((p=rindex(path,'/'))) *p=0;
+ PRINTF(L_CORE_DYN,"library path %sn",path);
+
+ char pat[32];
+ snprintf(pat,sizeof(pat),"%s*-%d%s%s",LIBSC_PREFIX,SCAPIVERS,SO_INDICATOR,APIVERSION);
+ bool res=true;
+ cReadDir dir(path);
+ struct dirent *e;
+ while((e=dir.Next())) {
+ if(!fnmatch(pat,e->d_name,FNM_PATHNAME|FNM_NOESCAPE)) {
+ cScDll *dll=new cScDll(AddDirectory(path,e->d_name));
+ if(dll) {
+ if(!dll->Load()) res=false;
+ Ins(dll);
+ }
+ }
+ }
+ free(path);
+ return res;
+}
+
+#endif
+
+// --- cScPlugin ---------------------------------------------------------------
+
+class cScPlugin : public cPlugin {
+private:
+ tI18nPhrase *phrases;
+#ifndef STATICBUILD
+ cScDlls dlls;
+#endif
+public:
+ cScPlugin(void);
+ virtual ~cScPlugin();
+ virtual const char *Version(void);
+ virtual const char *Description(void);
+ virtual const char *CommandLineHelp(void);
+ virtual bool ProcessArgs(int argc, char *argv[]);
+ virtual bool Initialize(void);
+ virtual bool Start(void);
+ virtual void Stop(void);
+ virtual void Housekeeping(void);
+ virtual cMenuSetupPage *SetupMenu(void);
+ virtual bool SetupParse(const char *Name, const char *Value);
+ virtual const char **SVDRPHelpPages(void);
+ virtual cString SVDRPCommand(const char *Command, const char *Option, int &ReplyCode);
+ };
+
+cScPlugin::cScPlugin(void)
+{
+ static const char *logg[] = { "off","active CAIDs","all CAIDs" };
+ ScOpts=new cOpts(0,5);
+ ScOpts->Add(new cOptSel ("AutoUpdate" ,"Update keys (AU)" ,&ScSetup.AutoUpdate,3,logg));
+ ScOpts->Add(new cOptBool ("ConcurrentFF" ,"Concurrent FF streams",&ScSetup.ConcurrentFF));
+ ScOpts->Add(new cOptBool ("LocalPriority","Prefer local systems" ,&ScSetup.LocalPriority));
+ ScOpts->Add(new cOptMInt ("ScCaps" ,"Active on DVB card" , ScSetup.ScCaps,MAXSCCAPS,0));
+ ScOpts->Add(new cOptMInt ("CaIgnore" ,"Ignore CAID" , ScSetup.CaIgnore,MAXCAIGN,2));
+ LogOpts=new cOpts(0,4);
+ LogOpts->Add(new cOptBool ("LogConsole" ,"Log to console" ,&logcfg.logCon));
+ LogOpts->Add(new cOptBool ("LogFile" ,"Log to file" ,&logcfg.logFile));
+ LogOpts->Add(new cOptStr ("LogFileName" ,"Filename" ,logcfg.logFilename,sizeof(logcfg.logFilename),FileNameChars));
+ LogOpts->Add(new cOptBool ("LogSyslog" ,"Log to syslog" ,&logcfg.logSys));
+ phrases=0;
+#ifndef STATICBUILD
+ dlls.Load();
+#endif
+ cScDvbDevice::Capture();
+}
+
+cScPlugin::~cScPlugin()
+{
+ delete ScOpts;
+ delete LogOpts;
+}
+
+bool cScPlugin::Initialize(void)
+{
+ PRINTF(L_GEN_INFO,"SC version %s initializing",ScVersion);
+ return cScDvbDevice::Initialize();
+}
+
+bool cScPlugin::Start(void)
+{
+ PRINTF(L_GEN_INFO,"SC version %s starting",ScVersion);
+ if(APIVERSNUM<MINAPIVERSNUM) {
+ PRINTF(L_GEN_ERROR,"SC plugin needs at least VDR API version %d.%d.%d",MIN_VERS,MIN_MAJOR,MIN_MINOR);
+ return false;
+ }
+ if(sizeof(int)!=4) {
+ PRINTF(L_GEN_ERROR,"compiled with 'int' as %d bit. Only supporting 32 bit.",(int)sizeof(int)*8);
+ return false;
+ }
+ if(sizeof(long long)!=8) {
+ PRINTF(L_GEN_ERROR,"compiled with 'long long' as %d bit. Only supporting 64 bit.",(int)sizeof(long long)*8);
+ return false;
+ }
+
+ ScPlugin=this;
+ Feature.AddPhrases(ScPhrases);
+ RegisterI18n((phrases=Feature.GetPhrases()));
+ filemaps.SetCfgDir(ConfigDirectory());
+ ScSetup.Check();
+ if(!cSoftCAM::Load(ConfigDirectory())) return false;
+ if(Feature.SmartCard()) {
+#ifdef DEFAULT_PORT
+ smartcards.AddPort(DEFAULT_PORT);
+#endif
+ smartcards.LaunchWatcher();
+ }
+ cScDvbDevice::Startup();
+ return true;
+}
+
+void cScPlugin::Stop(void)
+{
+ cScDvbDevice::Shutdown();
+ LogStatsDown();
+ cSoftCAM::Shutdown();
+ RegisterI18n(NULL); free(phrases);
+ PRINTF(L_GEN_DEBUG,"SC cleanup done");
+}
+
+const char *cScPlugin::Version(void)
+{
+ return ScVersion;
+}
+
+const char *cScPlugin::Description(void)
+{
+ return tr("A software emulated CAM");
+}
+
+const char *cScPlugin::CommandLineHelp(void)
+{
+ static char *help_str=0;
+
+ free(help_str); // for easier orientation, this is column 80|
+ asprintf(&help_str," -B N --budget=N forces DVB device N to budget mode (using FFdecsa)\n"
+ " -I --inverse-cd use inverse CD detection for the next serial device\n"
+ " -R --inverse-rst use inverse RESET for the next serial device\n"
+ " -C FREQ --clock=FREQ use FREQ as clock for the card reader on the next\n"
+ " serial device (rather than 3.5712 MHz\n"
+ " -s DEV, --serial=DEV activate Phoenix ISO interface on serial device DEV\n"
+ " (default: %s)\n"
+ " -d CMD, --dialup=CMD call CMD to start/stop dialup-network\n"
+ " (default: %s)\n"
+ " -t SECS, --timeout=SECS shutdown timeout for dialup-network\n"
+ " (default: %d secs)\n",
+ "none","none",netTimeout/1000
+ );
+ return help_str;
+}
+
+bool cScPlugin::ProcessArgs(int argc, char *argv[])
+{
+ static struct option long_options[] = {
+ { "serial", required_argument, NULL, 's' },
+ { "inverse-cd", no_argument, NULL, 'I' },
+ { "inverse-rst", no_argument, NULL, 'R' },
+ { "clock", required_argument, NULL, 'C' },
+ { "dialup", required_argument, NULL, 'd' },
+ { "external-au", required_argument, NULL, 'E' },
+ { "budget", required_argument, NULL, 'B' },
+ { NULL }
+ };
+
+ int c, option_index=0;
+ bool invCD=false, invRST=false;
+ int clock=0;
+ while((c=getopt_long(argc,argv,"d:s:t:B:C:E:IR",long_options,&option_index))!=-1) {
+ switch (c) {
+ case 'I': invCD=true; break;
+ case 'R': invRST=true; break;
+ case 'C': clock=atoi(optarg); break;
+ case 's': smartcards.AddPort(optarg,invCD,invRST,clock); invCD=false; invRST=false; clock=0; break;
+ case 'd': netscript=optarg; break;
+ case 't': netTimeout=atoi(optarg)*1000; break;
+ case 'E': externalAU=optarg; break;
+ case 'B': cScDvbDevice::SetForceBudget(atoi(optarg)); break;
+ default: return false;
+ }
+ }
+ return true;
+}
+
+cMenuSetupPage *cScPlugin::SetupMenu(void)
+{
+ return new cMenuSetupSc(ConfigDirectory());
+}
+
+bool cScPlugin::SetupParse(const char *Name, const char *Value)
+{
+ if((ScOpts && ScOpts->Parse(Name,Value)) ||
+ (LogOpts && LogOpts->Parse(Name,Value)) ||
+ cSystems::ConfigParse(Name,Value)) ;
+ else if(!strcasecmp(Name,"LogConfig")) cLogging::ParseConfig(Value);
+ else return false;
+ return true;
+}
+
+void cScPlugin::Housekeeping(void)
+{
+ for(int n=cDevice::NumDevices(); --n>=0;) {
+ cScDvbDevice *dev=dynamic_cast<cScDvbDevice *>(cDevice::GetDevice(n));
+ if(dev && dev->Cam()) dev->Cam()->HouseKeeping();
+ }
+ if(Feature.KeyFile()) keys.HouseKeeping();
+}
+
+const char **cScPlugin::SVDRPHelpPages(void)
+{
+ static const char *HelpPages[] = {
+ "RELOAD\n"
+ " Reload all configuration files.",
+ "LOG <on|off> <class>[,<class>...]\n"
+ " Turn the given message class(es) on or off.",
+ "LOGCFG\n"
+ " Display available message classes and their status.",
+ NULL
+ };
+ return HelpPages;
+}
+
+cString cScPlugin::SVDRPCommand(const char *Command, const char *Option, int &ReplyCode)
+{
+ if(!strcasecmp(Command,"RELOAD")) {
+ if(cSoftCAM::Active()) {
+ ReplyCode=550;
+ return "Softcam active. Can't reload files now";
+ }
+ else {
+ if(cSoftCAM::Load(ConfigDirectory()))
+ return "Files reloaded successfully";
+ else {
+ ReplyCode=901;
+ return "Reloading files not entirely successfull";
+ }
+ }
+ }
+ else if(!strcasecmp(Command,"LOG")) {
+ if(Option && *Option) {
+ char *opt=strdup(Option);
+ opt=skipspace(opt);
+ bool mode;
+ if(!strncasecmp(opt,"ON ",3)) { mode=true; opt+=3; }
+ else if(!strncasecmp(opt,"OFF ",4)) { mode=false; opt+=4; }
+ else { ReplyCode=501; return "Bad mode, valid: on off"; }
+ do {
+ char *s=index(opt,',');
+ if(s) *s++=0;
+ int c=cLogging::GetClassByName(opt);
+ if(c>=0) cLogging::SetModuleOption(c,mode);
+ else { ReplyCode=501; return "Unknown message class"; }
+ opt=s;
+ } while(opt);
+ ScSetup.Store(true);
+ Setup.Save();
+ return "Done";
+ }
+ else { ReplyCode=501; return "Missing args"; }
+ }
+ else if(!strcasecmp(Command,"LOGCFG")) {
+ cLineBuff lb(256);
+ for(int m=1; m<LMOD_MAX; m++) {
+ const char *name=cLogging::GetModuleName(LCLASS(m,0));
+ if(name) {
+ int o=cLogging::GetModuleOptions(LCLASS(m,0));
+ if(o>=0) {
+ for(int i=0; i<LOPT_NUM; i++) {
+ const char *opt;
+ if(i==0) opt="enable";
+ else opt=cLogging::GetOptionName(LCLASS(m,1<<i));
+ if(opt)
+ lb.Printf("%s.%s %s\n",name,opt,(o&(1<<i))?"on":"off");
+ }
+ }
+ }
+ }
+ if(lb.Length()>0) return lb.Line();
+ ReplyCode=901; return "No config available";
+ }
+ return NULL;
+}
+
+VDRPLUGINCREATOR(cScPlugin); // Don't touch this!
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SC_H
+#define ___SC_H
+
+class cEcmInfo;
+class cLogHook;
+
+// ----------------------------------------------------------------
+
+class cSoftCAM {
+public:
+ static bool Load(const char *cfgdir);
+ static void Shutdown(void);
+ //
+ static bool Active(void);
+ static char *CurrKeyStr(int CardNum, int num);
+ static void SetLogStatus(int CardNum, const cEcmInfo *ecm, bool on);
+ static void AddHook(int CardNum, cLogHook *hook);
+ static bool TriggerHook(int CardNum, int id);
+ };
+
+#endif // ___SC_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SC_SETUP_H
+#define ___SC_SETUP_H
+
+#define MAXSCCAPS 10
+#define MAXCAIGN 16
+
+// ----------------------------------------------------------------
+
+class cScSetup {
+public:
+ int AutoUpdate;
+ int ScCaps[MAXSCCAPS];
+ int ConcurrentFF;
+ int CaIgnore[MAXCAIGN];
+ int LocalPriority;
+public:
+ cScSetup(void);
+ void Check(void);
+ void Store(bool AsIs);
+ bool CapCheck(int n);
+ bool Ignore(unsigned short caid);
+ };
+
+extern cScSetup ScSetup;
+
+#endif
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <unistd.h>
+#include <termios.h>
+#include <linux/serial.h>
+#include <ctype.h>
+
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+
+#include "smartcard.h"
+#include "misc.h"
+#include "log-core.h"
+
+#define DATAFILE "smartcard.conf"
+#define ISO_FREQ 3571200 // Hz
+
+//#define SER_EMU // use serial emulation (select one of the following)
+//#define EMU_SECA // fake Seca card
+//#define EMU_IRD // fake Irdeto/Beta card
+//#define EMU_IRD_384 // fake ACS 384, if undefined ACS 383
+//#define EMU_CRYPTO // fake Cryptoworks card
+//#define NO_PTS_PROTO // disable PTS protocol (baudrate changes)
+
+struct BaudRates {
+ int real;
+ speed_t apival;
+ };
+
+static const struct BaudRates BaudRateTab[] = {
+ { 9600, B9600 },
+ { 19200, B19200 },
+ { 38400, B38400 },
+ { 57600, B57600 },
+ { 115200, B115200 },
+ { 230400, B230400 }
+ };
+
+// -- cSerial ------------------------------------------------------------------
+
+class cSerial {
+private:
+ char *devName;
+ int fd;
+ int currMode, statInv;
+ bool invRST;
+ //
+ speed_t FindBaud(int baud);
+#ifdef SER_EMU
+ unsigned char devBuff[1024], nextSW[SB_LEN];
+ int buffCount, dataLen, record;
+ bool cmdStart, rts, dsr, flag;
+ int remTime;
+#endif
+public:
+ cSerial(const char *DevName, bool invCD, bool InvRST);
+ ~cSerial();
+ bool Open(void);
+ bool SetMode(int mode, int baud=9600);
+ int CurrentMode(void) const { return currMode; }
+ void Flush(void);
+ int Read(unsigned char *mem, int len, int timeout, int initialTimeout=0);
+ int Write(const unsigned char *mem, int len, int delay=0);
+ void ToggleRTS(void);
+ bool CheckCAR(void);
+ void Close(void);
+ const char *DeviceName(void) const { return devName; }
+ };
+
+cSerial::cSerial(const char *DevName, bool invCD, bool InvRST)
+{
+ devName=strdup(DevName);
+ statInv=invCD ? TIOCM_CAR:0;
+ invRST=InvRST;
+ fd=-1; currMode=SM_NONE;
+}
+
+cSerial::~cSerial()
+{
+ Close();
+ free(devName);
+}
+
+#ifndef SER_EMU
+
+bool cSerial::Open(void)
+{
+ PRINTF(L_CORE_SERIAL,"%s: open serial port",devName);
+ fd=open(devName,O_RDWR|O_NONBLOCK|O_NOCTTY);
+ if(fd>=0) {
+ PRINTF(L_CORE_SERIAL,"%s: set DTR/RTS",devName);
+ unsigned int modembits;
+ modembits=TIOCM_DTR; CHECK(ioctl(fd, TIOCMBIS, &modembits));
+ modembits=TIOCM_RTS; CHECK(ioctl(fd, invRST?TIOCMBIS:TIOCMBIC, &modembits));
+ PRINTF(L_CORE_SERIAL,"%s: init done",devName);
+ return true;
+ }
+ else PRINTF(L_GEN_ERROR,"%s: open failed: %s",devName,strerror(errno));
+ return false;
+}
+
+void cSerial::Close(void)
+{
+ if(fd>=0) {
+ PRINTF(L_CORE_SERIAL,"%s: shutting down",devName);
+ Flush();
+ unsigned int modembits=0;
+ CHECK(ioctl(fd,TIOCMSET,&modembits));
+ close(fd); fd=-1;
+ PRINTF(L_CORE_SERIAL,"%s: shutdown done",devName);
+ }
+}
+
+speed_t cSerial::FindBaud(int baud)
+{
+ for(int i=0; i<(int)(sizeof(BaudRateTab)/sizeof(struct BaudRates)); i++) {
+ int b=BaudRateTab[i].real;
+ int d=((b-baud)*10000)/b;
+ if(abs(d)<=300) {
+ PRINTF(L_CORE_SERIAL,"%s: requested baudrate %d -> %d (%+.2f%%)",devName,baud,b,(float)d/100.0);
+ return BaudRateTab[i].apival;
+ }
+ }
+ PRINTF(L_CORE_SERIAL,"%s: requested baudrate %d -> custom",devName,baud);
+ return B0;
+}
+
+bool cSerial::SetMode(int mode, int baud)
+{
+ if(fd>=0) {
+ speed_t bconst=FindBaud(baud);
+ bool custom=false;
+ if(bconst==B0) { custom=true; bconst=B38400; }
+
+ struct termios tio;
+ memset(&tio,0,sizeof(tio));
+ LBSTARTF(L_CORE_SERIAL);
+ LBPUT("%s: set serial options: %d,",devName,baud);
+ tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL);
+ if(!(mode&SM_1SB)) tio.c_cflag |= CSTOPB;
+ tio.c_iflag = (INPCK | BRKINT);
+ tio.c_cc[VMIN] = 1;
+ cfsetispeed(&tio,bconst);
+ cfsetospeed(&tio,bconst);
+ switch(mode&SM_MASK) {
+ case SM_8E2:
+ LBPUT("8e%d",(mode&SM_1SB)?1:2);
+ tio.c_cflag |= PARENB; break;
+ case SM_8N2:
+ LBPUT("8n%d",(mode&SM_1SB)?1:2);
+ break;
+ case SM_8O2:
+ LBPUT("8o%d",(mode&SM_1SB)?1:2);
+ tio.c_cflag |= (PARENB | PARODD); break;
+ default:
+ LBPUT("BAD MODE");
+ return false;
+ }
+ LBEND();
+
+ struct serial_struct s;
+ if(ioctl(fd,TIOCGSERIAL,&s)<0) {
+ PRINTF(L_GEN_ERROR,"%s: get serial failed: %s",devName,strerror(errno));
+ return false;
+ }
+ if(!custom && ((s.flags&ASYNC_SPD_MASK)==ASYNC_SPD_CUST || s.custom_divisor!=0)) {
+ s.custom_divisor=0;
+ s.flags &= ~ASYNC_SPD_MASK;
+ if(ioctl(fd,TIOCSSERIAL,&s)<0) {
+ PRINTF(L_GEN_ERROR,"%s: set serial failed: %s",devName,strerror(errno));
+ return false;
+ }
+ }
+ if(!tcsetattr(fd,TCSANOW,&tio)) {
+ if(custom) {
+ s.custom_divisor=(s.baud_base+(baud/2))/baud;
+ s.flags=(s.flags&~ASYNC_SPD_MASK) | ASYNC_SPD_CUST;
+ PRINTF(L_CORE_SERIAL,"%s: custom: baud_base=%d baud=%d devisor=%d -> effective baudrate %d (%+.2f%% off)",devName,s.baud_base,baud,s.custom_divisor,s.baud_base/s.custom_divisor,(float)(s.baud_base/s.custom_divisor-baud)/(float)baud);
+ if(ioctl(fd,TIOCSSERIAL,&s)<0) {
+ PRINTF(L_GEN_ERROR,"%s: set serial failed: %s",devName,strerror(errno));
+ return false;
+ }
+ }
+ currMode=mode; Flush();
+ return true;
+ }
+ else PRINTF(L_GEN_ERROR,"%s: tcsetattr failed: %s",devName,strerror(errno));
+ }
+ return false;
+}
+
+void cSerial::Flush(void)
+{
+ if(fd>=0)
+ CHECK(tcflush(fd,TCIOFLUSH));
+}
+
+int cSerial::Read(unsigned char *mem, int len, int timeout, int initialTimeout)
+{
+ PRINTF(L_CORE_SERIAL,"%s: read len=%d timeout=%d:%d",devName,len,timeout,initialTimeout);
+ bool incomplete=false;
+ if(len<0) { len=-len; incomplete=true; }
+ int to=initialTimeout>0 ? initialTimeout : timeout;
+ int n=0;
+ while(n<len) {
+ int r=read(fd,mem+n,len-n);
+ if(r<=0) {
+ if(r==0) PRINTF(L_CORE_SERIAL,"%s: read bogus eof",devName);
+ if(errno==EAGAIN) {
+ struct pollfd u;
+ u.fd=fd; u.events=POLLIN;
+ r=poll(&u,1,to);
+ if(r<0) {
+ PRINTF(L_GEN_ERROR,"%s: read poll failed: %s",devName,strerror(errno));
+ return -1;
+ }
+ else if(r==0) { // timeout
+ PRINTF(L_CORE_SERIAL,"%s: read timeout (%d ms)",devName,to);
+ if(n>0 && incomplete) break; // return bytes read so far
+ return -2;
+ }
+ continue;
+ }
+ else {
+ PRINTF(L_GEN_ERROR,"%s: read failed: %s",devName,strerror(errno));
+ return -1;
+ }
+ }
+ n+=r; to=timeout;
+ }
+ HEXDUMP(L_CORE_SERIAL,mem,n,"%s: read data",devName);
+ return n;
+}
+
+int cSerial::Write(const unsigned char *mem, int len, int delay)
+{
+ PRINTF(L_CORE_SERIAL,"%s: write len=%d delay=%d",devName,len,delay);
+ HEXDUMP(L_CORE_SERIAL,mem,len,"%s: write data",devName);
+ Flush();
+ int n=0;
+ while(n<len) {
+ struct pollfd u;
+ u.fd=fd; u.events=POLLOUT;
+ int r;
+ do {
+ r=poll(&u,1,500);
+ if(r<0) {
+ PRINTF(L_GEN_ERROR,"%s: write poll failed: %s",devName,strerror(errno));
+ return -1;
+ }
+ else if(r==0) { // timeout
+ PRINTF(L_CORE_SERIAL,"%s: write timeout",devName);
+ return -2;
+ }
+ } while(r<1);
+ r=write(fd,mem+n,delay>0?1:len-n);
+ if(r<0 && errno!=EAGAIN) {
+ PRINTF(L_GEN_ERROR,"%s: write failed: %s",devName,strerror(errno));
+ return -1;
+ }
+ if(r>0) n+=r;
+ if(delay>0) cCondWait::SleepMs(delay);
+ }
+ return n;
+}
+
+void cSerial::ToggleRTS(void)
+{
+ int mask=0;
+ if(ioctl(fd,TIOCMGET,&mask)<0) { LOG_ERROR; return; }
+ if(mask&TIOCM_RTS) { mask&=~TIOCM_RTS; PRINTF(L_CORE_SERIAL,"%s: toggle RTS, now off",devName); }
+ else { mask|= TIOCM_RTS; PRINTF(L_CORE_SERIAL,"%s: toggle RTS, now on",devName); }
+ CHECK(ioctl(fd,TIOCMSET,&mask));
+ Flush();
+}
+
+bool cSerial::CheckCAR(void)
+{
+ int status=0;
+ if(ioctl(fd,TIOCMGET,&status)<0) { LOG_ERROR; return false; }
+ PRINTF(L_CORE_SERIAL,"%s: CAR is %sactive (lines:%s%s%s%s%s%s%s%s%s)",
+ devName,(status&TIOCM_CAR)?"in":"",
+ (status&TIOCM_LE)?" LE":"", (status&TIOCM_DTR)?" DTR":"",
+ (status&TIOCM_RTS)?" RTS":"",(status&TIOCM_ST)?" ST":"",
+ (status&TIOCM_SR)?" SR":"", (status&TIOCM_CTS)?" CTS":"",
+ (status&TIOCM_CAR)?" CAR":"",(status&TIOCM_RNG)?" RNG":"",
+ (status&TIOCM_DSR)?" DSR":"" );
+ status^=statInv; // invert status for broken cardreaders
+ return !(status & TIOCM_CAR);
+}
+
+#else //SER_EMU
+
+//
+// emulator for a serial phoenix interface
+// (activate in smartcard.h)
+//
+
+bool cSerial::Open(void)
+{
+ PRINTF(L_CORE_SERIAL,"serial: open serial port %s (emulator)",devName);
+ PRINTF(L_CORE_SERIAL,"serial: init done");
+ fd=1; buffCount=0; cmdStart=true; rts=false; flag=false;
+ remTime=time(0); dsr=true;
+ return true;
+}
+
+void cSerial::Close(void)
+{
+ if(fd>=0) {
+ PRINTF(L_CORE_SERIAL,"serial: shutting down ... ");
+ fd=-1;
+ PRINTF(L_CORE_SERIAL,"done");
+ }
+}
+
+speed_t cSerial::FindBaud(int baud)
+{
+ return B0;
+}
+
+bool cSerial::SetMode(int mode, int baud)
+{
+ currMode=mode;
+ Flush();
+ return true;
+}
+
+void cSerial::Flush(void)
+{
+ buffCount=0;
+}
+
+#define PUSH(mem,len) { memcpy(devBuff+buffCount,(mem),(len)); buffCount+=(len); }
+#define PUSHB(b) { devBuff[buffCount++]=(b); }
+
+int cSerial::Read(unsigned char *mem, int len, int timeout, int initialTimeout)
+{
+ PRINTF(L_CORE_SERIAL,"serial: read len=%d timeout=%d",len,timeout);
+ if(len<0) {
+ len=-len;
+ if(buffCount<len) len=buffCount;
+ }
+ if(buffCount<len) return -2; // timeout
+ memcpy(mem,devBuff,len);
+ memmove(devBuff,devBuff+len,buffCount-len);
+ buffCount-=len;
+ return len;
+}
+
+int cSerial::Write(const unsigned char *mem, int len, int delay)
+{
+ PRINTF(L_CORE_SERIAL,"serial: write len=%d delay=%d",len,delay);
+ Flush();
+ cCondWait::SleepMs(300);
+ PUSH(mem,len); // echo back
+#if defined(EMU_SECA)
+ if(cmdStart && len==CMD_LEN) { // new INS
+ PUSHB(mem[INS_IDX]); // ACK byte
+ cmdStart=false;
+ dataLen=mem[LEN_IDX];
+ unsigned char data[256];
+ memset(data,0,sizeof(data));
+ nextSW[0]=0x90; nextSW[1]=0x00;
+ bool readcmd=false;
+ switch(mem[0]*256+mem[1]) {
+ case 0xc13a:
+ case 0xc10e:
+ readcmd=true;
+ break;
+ case 0xc116:
+ data[3]=0x04;
+ readcmd=true;
+ break;
+ case 0xc112:
+ data[1]=0x64;
+ data[22]=0x1d; data[23]=0x84;
+ readcmd=true;
+ break;
+ case 0xc132:
+ data[1]=0xFF; data[2]=0xFF; data[3]=0xFF; data[4]=0xFF;
+ data[5]=0xFF; data[6]=0xFF; data[7]=0xFF; data[8]=0xFF;
+ readcmd=true;
+ break;
+ }
+ if(readcmd) {
+ PUSH(data,dataLen);
+ PUSH(nextSW,2);
+ cmdStart=true;
+ }
+ }
+ else {
+ dataLen-=len;
+ if(dataLen<=0 && !cmdStart) {
+ PUSH(nextSW,2);
+ cmdStart=true;
+ }
+ }
+#elif defined(EMU_CRYPTO)
+ if(cmdStart && len==CMD_LEN) { // new INS
+ cmdStart=false;
+ dataLen=mem[LEN_IDX];
+ unsigned char data[256];
+ memset(data,0,sizeof(data));
+ nextSW[0]=0x90; nextSW[1]=0x00;
+ bool readcmd=false;
+ switch(mem[0]*256+mem[1]) {
+ case 0xa4a4:
+ switch(mem[5]*256+mem[6]) {
+ case 0x2f01:
+ case 0x0f00:
+ case 0x0f20:
+ //case 0x0f40:
+ case 0x0f60:
+ case 0x0e11:
+ case 0x1f88:
+ case 0x1f8c: nextSW[0]=0x9f; nextSW[1]=0x11; break;
+ default: nextSW[0]=0x94; nextSW[1]=0x04; break;
+ }
+ break;
+ case 0xa4a2:
+ if(mem[2]==1) {
+ if(mem[4]==3) {
+ record=0x01;
+ nextSW[0]=0x9f; nextSW[1]=0x26;
+ }
+ else {
+ record=0x02;
+ nextSW[0]=0x9f; nextSW[1]=0x42;
+ }
+ }
+ else {
+ record=mem[5];
+ switch(record) {
+ case 0x80: nextSW[0]=0x9f; nextSW[1]=0x07; break;
+ case 0xD1: nextSW[0]=0x9f; nextSW[1]=0x04; break;
+ case 0x9F: nextSW[0]=0x9f; nextSW[1]=0x03; break;
+ case 0x9E: nextSW[0]=0x9f; nextSW[1]=0x42; break;
+ case 0xD6:
+ case 0xC0: nextSW[0]=0x9f; nextSW[1]=0x12; break;
+ default: nextSW[0]=0x94; nextSW[1]=0x02; break;
+ }
+ }
+ break;
+ case 0xa4b2:
+ readcmd=true;
+ switch(record) {
+ case 0x01:
+ if(mem[3]==1) {
+ nextSW[0]=0x94; nextSW[1]=0x02;
+ dataLen=0;
+ }
+ else {
+ dataLen=0x26;
+ static const unsigned char resp[0x26] = {
+ 0x83,0x01,0x88,
+ 0x8c,0x03,0x40,0x30,0x40,
+ 0x8d,0x04,0x16,0xac,0x16,0xba,
+ 0xd5,0x10,'D','u','m','m','y',0,0,0,0,0,0,0,0x12,0x34,0x56,0x67,
+ 0x8f,0x01,0x55,
+ 0x91,0x01,0x40
+ };
+ memcpy(data,resp,sizeof(resp));
+ }
+ break;
+ case 0x02:
+ if(mem[3]==1) {
+ nextSW[0]=0x94; nextSW[1]=0x02;
+ dataLen=0;
+ }
+ else {
+ dataLen=0x42;
+ static const unsigned char resp[0x42] = {
+ 0x83,0x01,0x88,
+ 0x8c,0x03,0xA0,0x30,0x40,
+ 0x8d,0x04,0x16,0xac,0x16,0xba,
+ 0xd5,0x10,'D','u','m','m','y',0,0,0,0,0,0,0,0x12,0x34,0x56,0x67,
+ 0x92,0x20,0x00 // ...
+ };
+ memcpy(data,resp,sizeof(resp));
+ }
+ break;
+ case 0x80:
+ dataLen=0x07;
+ data[0]=0x80;
+ data[1]=0x05;
+ data[2]=0x5c;
+ data[3]=0x0c;
+ data[4]=0x00;
+ data[5]=0xec;
+ data[6]=0x0a;
+ break;
+ case 0xD1:
+ dataLen=0x04;
+ data[0]=0xd1;
+ data[1]=0x02;
+ data[2]=0x0d;
+ data[3]=0x22;
+ break;
+ case 0x9F:
+ dataLen=0x03;
+ data[0]=0x9f;
+ data[1]=0x01;
+ data[2]=0x88;
+ break;
+ case 0x9E:
+ {
+ dataLen=0x42;
+ static const unsigned char resp[0x42] = {
+ 0x9E,0x40,
+ 0x28,0xFE, /// ...
+ };
+ memcpy(data,resp,sizeof(resp));
+ flag=true;
+ break;
+ }
+ case 0xC0:
+ case 0xD6:
+ dataLen=0x12;
+ data[0]=record;
+ data[1]=0x10;
+ memset(data+2,0,16);
+ strcpy((char *)data+2,"Dum Issuer");
+ break;
+ }
+ break;
+ case 0xa4b8:
+ readcmd=true;
+ if(mem[2]==0) {
+ dataLen=0x0C;
+ memset(data,0,0x0c);
+ data[0]=0xDF;
+ data[1]=0x0A;
+ data[4]=0xDF;
+ data[5]=0x88;
+ }
+ else {
+ dataLen=0;
+ nextSW[0]=0x94; nextSW[1]=0x02;
+ }
+ break;
+ case 0xa44c:
+ nextSW[0]=0x9f; nextSW[1]=flag ? 0x42 : 0x1c;
+ break;
+ case 0xa4c0:
+ readcmd=true;
+ if(mem[4]==0x1c) {
+ data[0]=0xdb;
+ data[1]=0x10;
+ memset(&data[2],0x45,16);
+ data[18]=0xdf;
+ data[19]=0x08;
+ data[20]=0x50;
+ data[23]=0x80;
+ }
+ else if(mem[4]==0x11) {
+ memset(data,0,0x11);
+ data[0]=0xdf;
+ data[1]=0x0f;
+ data[6]=0x3f;
+ data[7]=0x6a;
+ }
+ else if(mem[4]==0x42) {
+ static const unsigned char resp[0x42] = {
+ 0xdf,0x40,
+ 0x6d,0xbb,0x81, // ...
+ };
+ memcpy(data,resp,sizeof(resp));
+ }
+ break;
+ }
+ if(readcmd) {
+ if(dataLen>0) {
+ PUSHB(mem[INS_IDX]); // ACK byte
+ PUSH(data,dataLen);
+ }
+ PUSH(nextSW,2);
+ cmdStart=true;
+ }
+ else {
+ PUSHB(mem[INS_IDX]); // ACK byte
+ }
+ }
+ else {
+ dataLen-=len;
+ if(dataLen<=0 && !cmdStart) {
+ PUSH(nextSW,2);
+ cmdStart=true;
+ }
+ }
+#elif defined(EMU_IRD)
+ static const struct Resp {
+ unsigned int cmd;
+ unsigned char data[MAX_LEN];
+ } resp[] = {
+#ifdef EMU_IRD_384
+ { 0x01020203,
+ {0x01,0x02,0x00,0x00,0x02,0x00,0x00,0x10,0x03,0x84,0x00,0x00,0x00,0x17,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x45,0x52} },
+ { 0x01020E02,
+ {0x01,0x02,0x00,0x00,0x0e,0x02,0x00,0x40,0xa9,0x6d,0x73,0x97,0x9e,0xfc,0x9b,0x8e,0x5b,0x8c,0xfa,0xb2,0x0c,0xb2,0x57,0x0f,0xb2,0xf7,0x29,0x4e,0xa2,0xcb,0xbd,0x5b,0x52,0x74,0xb1,0x2a,0xb7,0xc5,0xd9,0x62,0x6d,0x37,0x6d,0x9d,0xa3,0xe9,0x61,0xbb,0x1b,0x2b,0x56,0xb7,0x86,0x0c,0xe6,0xb1,0x07,0x6f,0xe0,0xf8,0x8a,0xd3,0x05,0x83,0xf6,0x53,0x0e,0xd2,0x72,0xd5,0xc1,0x50} },
+ { 0x01020E03,
+ {0x01,0x02,0x00,0x00,0x0e,0x03,0x00,0x40,0xb6,0xde,0xa8,0xce,0x86,0x1c,0x42,0x72,0xa8,0x16,0x4b,0xf9,0xce,0x33,0xb5,0x43,0xd0,0x50,0xe6,0xa7,0xf1,0xcb,0x55,0x25,0x97,0x13,0xee,0x62,0x98,0xe7,0x17,0x50,0xeb,0x3b,0x59,0x10,0x0a,0xb6,0x2e,0x93,0x61,0x71,0x3c,0xe6,0xe2,0x2c,0x1e,0x7d,0xbd,0x6a,0x49,0xbb,0x04,0x5b,0xdf,0x2f,0xb7,0xa7,0x93,0xe0,0x3d,0x40,0x4e,0xd1} },
+#else
+ { 0x01020203,
+ {0x01,0x02,0x00,0x00,0x02,0x00,0x00,0x10,0x03,0x83,0x00,0x00,0x00,0x17,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x47,0x45,0x52} },
+ { 0x01020E02,
+ {0x01,0x02,0x00,0x00,0x0E,0x02,0x00,0x40,0x65,0xEF,0xD0,0xA7,0xF3,0x80,0x48,0x0E,0xC1,0x4B,0x41,0x6C,0xDB,0x68,0x47,0xE4,0x23,0xAD,0x96,0x12,0x07,0x58,0x58,0x29,0xE9,0x14,0x27,0x7F,0x7D,0xF8,0xC2,0x65,0x76,0x4D,0x75,0x04,0x7C,0x9B,0xAA,0x99,0x58,0xEA,0xE2,0x43,0xB5,0x03,0x05,0xD6,0x62,0x99,0xF5,0x18,0x16,0x4E,0xCF,0x49,0x11,0xBD,0xF3,0xEE,0xC3,0xCD,0x90,0x3B} },
+ { 0x01020E03,
+ {0x01,0x02,0x00,0x00,0x0E,0x03,0x00,0x40,0x9B,0x06,0xB5,0x0A,0x98,0xC6,0x2E,0x1D,0x71,0xA1,0xE8,0x84,0xAE,0x98,0x57,0xE9,0xE6,0xC2,0x97,0x46,0x25,0x7A,0x2B,0xA1,0xD5,0x33,0x18,0xDE,0x16,0xC1,0xAB,0x22,0x2C,0xC2,0x11,0x24,0x81,0x11,0xA8,0x39,0xE3,0xB1,0xDB,0x33,0x1A,0x93,0x31,0xB0,0x61,0xD8,0xDE,0x92,0x1F,0x29,0x20,0xD0,0x9E,0x0F,0x6A,0xF0,0x7C,0xBA,0xCD,0xCC} },
+#endif
+ { 0x01020003,
+ {0x01,0x02,0x00,0x00,0x00,0x03,0x00,0x14,0x37,0x30,0x31,0x32,0x30,0x36,0x39,0x33,0x34,0x37,0x32,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} },
+ { 0x01020103,
+ {0x01,0x02,0x00,0x00,0x01,0x00,0x00,0x10,0x00,0x17,0x00,0x00,0x01,0x00,0x17,0x00,0x00,0x01,0x04,0x00,0xF3,0x86,0x01,0x1A} },
+ { 0x01021100,
+ {0x01,0x02,0x58,0x00,0x11,0x00,0x00,0x00} },
+ { 0x01020903,
+ {0x01,0x02,0x55,0x00,0x09,0x03,0x00,0x00} },
+ { 0x01020303,
+ {0x01,0x02,0x00,0x00,0x03,0x03,0x02,0x18,0x0F,0xFF,0xFF,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00} },
+ { 0x01020400,
+ {0x01,0x02,0x54,0x00,0x04,0x00,0x00,0x00} },
+ { 0x01050000,
+ {0x01,0x05,0x9D,0x00,0x38,0x00,0x02,0x16,0x00,0x01,0x00,0x13,0xFF,0xFF,0x22,0x88,0xBF,0x02,0x70,0xFA,0x5F,0x80,0xFD,0x1E,0xD4,0xD6,0xF0,0xF1,0x81,0xB3} },
+ { 0x01010000,
+ {0x01,0x01,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0xFF} },
+ { 0x00000000, // don't delete this one
+ {} }
+ };
+ const unsigned int cmd=(mem[0]<<24) + (mem[1]<<16) + (mem[2]<<8) + mem[3];
+ const struct Resp *r=&resp[0];
+ while(1) {
+ if(!r->cmd) {
+ static const unsigned char fail[] = { 0x01,0x99,0x6f };
+ PUSH(fail,sizeof(fail));
+ break;
+ }
+ if(cmd==r->cmd) {
+ const int len=r->data[7]+9-1;
+ int cs=0x3f;
+ for(int i=0 ; i<len ; i++) cs ^= r->data[i];
+ PUSH(r->data,len);
+ PUSHB(cs);
+ break;
+ }
+ r++;
+ }
+#else
+#error No serial emulation mode selected
+#endif
+ return len;
+}
+
+void cSerial::ToggleRTS(void)
+{
+ rts=!rts;
+ if(!rts) {
+#if defined(EMU_SECA)
+ static const unsigned char atr[] = { 0x3b,0x77,0x12,0x00,0x00,
+ 0x60,0x60,0x03,0x0E,0x6C,0xB6,0xD6 };
+#elif defined(EMU_IRD)
+ static const unsigned char atr[] = { 0x3B,0x9F,0x21,0x0E,
+#ifdef EMU_IRD_384
+ 0x49,0x52,0x44,0x45,0x54,0x4f,0x20,0x41,0x43,0x53,0x03,0x84,0x55,0xff,0x80,0x6d };
+#else
+ 0x49,0x52,0x44,0x45,0x54,0x4F,0x20,0x41,0x43,0x53,0x03,0x83,0x95,0x00,0x80,0x55 };
+#endif
+#elif defined(EMU_CRYPTO)
+ static const unsigned char atr[] = { 0x3B,0x78,0x12,0x00,0x00,
+ 0x65,0xC4,0x05,0x05,0x8F,0xF1,0x90,0x00 };
+#else
+#error No serial emulation mode selected
+#endif
+ PUSH(atr,sizeof(atr));
+ PRINTF(L_CORE_SERIAL,"serial: toggle RTS, now off");
+ }
+ else { PRINTF(L_CORE_SERIAL,"serial: toggle RTS, now on"); }
+}
+
+bool cSerial::CheckCAR(void)
+{
+#if 0
+ if(time(0)>=remTime+30) {
+ dsr=!dsr;
+ remTime=time(0);
+ }
+#endif
+ return dsr;
+}
+
+#endif //SER_EMU
+
+// -- cInfoStr -----------------------------------------------------------------
+
+cInfoStr::cInfoStr(void)
+:cLineBuff(256)
+{
+ current=0;
+}
+
+cInfoStr::~cInfoStr()
+{
+ free(current);
+}
+
+bool cInfoStr::Get(char *buff, int len)
+{
+ cMutexLock lock(&mutex);
+ if(current && current[0]) {
+ strn0cpy(buff,current,len);
+ return true;
+ }
+ return false;
+}
+
+void cInfoStr::Begin(void)
+{
+ Flush();
+}
+
+void cInfoStr::Finish()
+{
+ cMutexLock lock(&mutex);
+ free(current);
+ current=Grab();
+}
+
+// -- cSmartCard ---------------------------------------------------------------
+
+static const int Ftable[16] = {
+ 372,372,558,744,1116,1488,1860,0,0,512,768,1024,1536,2048,0,0
+ };
+
+static const float Dtable[16] = {
+ 0.0,1.0,2.0,4.0,8.0,16.0,0.0,0.0,
+ 0.0,0.0,0.5,0.25,0.125,0.0625,0.03125,0.015625
+ };
+
+cSmartCard::cSmartCard(const struct CardConfig *Cfg, const struct StatusMsg *Msg)
+{
+ cfg=Cfg; msg=Msg;
+ ser=0; atr=0; idStr[0]=0; cardUp=false; needsReset=true;
+}
+
+bool cSmartCard::GetCardIdStr(char *str, int len)
+{
+ strn0cpy(str,idStr,len);
+ return (str[0]!=0);
+}
+
+bool cSmartCard::GetCardInfoStr(char *str, int len)
+{
+ return infoStr.Get(str,len);
+}
+
+bool cSmartCard::Setup(cSerial *Ser, const struct Atr *Atr, int Id)
+{
+ ser=Ser; atr=Atr; id=Id;
+ if(cfg->SerMode==(ser->CurrentMode()&SM_MASK) && Init()) {
+ cardUp=true; needsReset=false;
+ return true;
+ }
+ return false;
+}
+
+#define NEED(x) { \
+ if(len+(x)>Atr->atrLen) { \
+ LBPUT("SHORT ATR"); \
+ return false; \
+ } \
+ }
+
+bool cSmartCard::ParseAtr(struct Atr *Atr, int id, int clock)
+{
+ // default values
+ Atr->histLen=0; Atr->T=0; Atr->F=372; Atr->D=1.0; Atr->N=0; Atr->WI=10;
+ Atr->BWI=4; Atr->CWI=0; Atr->Tspec=-1;
+
+ const unsigned char *atr=Atr->atr;
+ if(atr[0]==0x03) {
+ PRINTF(L_CORE_SC,"%d: indirect convention detected",id);
+ cSmartCard::Invert(Atr->atr,Atr->atrLen);
+ Atr->convention=SM_INDIRECT;
+ }
+ else if(atr[0]==0x3B) {
+ PRINTF(L_CORE_SC,"%d: direct convention detected",id);
+ Atr->convention=SM_DIRECT;
+ }
+ else {
+ PRINTF(L_CORE_SC,"%d: byte mode not supported 0x%02x",id,atr[0]);
+ return false;
+ }
+
+ // TS TO
+ Atr->histLen=atr[1]&0x0F;
+ int Y=atr[1]&0xF0, i=1, len=2;
+ LBSTARTF(L_CORE_SC);
+ LBPUT("%d: atr decoding TS=%02x hist=%d Y%d=%02x ",id,atr[0],Atr->histLen,i,Y);
+ do {
+ if(Y&0x10) { // TAi
+ NEED(1);
+ LBPUT("TA%d=%02x ",i,atr[len]);
+ if(i==1) {
+ Atr->TA1=atr[len];
+ Atr->F=Ftable[(atr[len]>>4)&0x0F];
+ Atr->D=Dtable[ atr[len] &0x0F];
+ LBPUT("F=%d D=%f ",Atr->F,Atr->D);
+ }
+ else if(i==2) {
+ Atr->Tspec=atr[len]&0x0F;
+ LBPUT("Tspec=%d ",Atr->Tspec);
+ }
+ else if(i==3) {
+ LBPUT("IFSC=%d ",atr[len]);
+ }
+ len++;
+ }
+ if(Y&0x20) { // TBi
+ NEED(1);
+ LBPUT("TB%d=%02x ",i,atr[len]);
+ if(i==3) {
+ Atr->BWI=(atr[len]>>4)&0x0F;
+ Atr->CWI=atr[len]&0x0F;
+ LBPUT("BWI=%d CWI=%d ",Atr->BWI,Atr->CWI);
+ }
+ len++;
+ }
+ if(Y&0x40) { // TCi
+ NEED(1);
+ LBPUT("TC%d=%02x ",i,atr[len]);
+ if(i==1) {
+ Atr->N=atr[len];
+ LBPUT("N=%d ",Atr->N);
+ }
+ else if(i==2) {
+ Atr->WI=atr[len];
+ LBPUT("WI=%d ",Atr->WI);
+ }
+ else if(i==3) {
+ LBPUT("CHK=%s ",atr[len]&1 ? "CRC16":"LRC");
+ }
+ len++;
+ }
+ if(Y&0x80) { // TDi
+ NEED(1);
+ LBPUT("TD%d=%02x ",i,atr[len]);
+ if(i==1) {
+ Atr->T=atr[len]&0x0F;
+ LBPUT("T=%d ",Atr->T);
+ }
+ else {
+ LBPUT("Tn=%d ",atr[len]&0x0F);
+ }
+ Y=atr[len]&0xF0;
+ len++;
+ }
+ else Y=0;
+ i++;
+ LBPUT("Y%d=%02x ",i,Y);
+ } while(Y);
+ NEED(Atr->histLen);
+ LBEND();
+
+ LBSTART(L_CORE_SERIAL);
+ LBPUT("%d: historical:",id);
+ for(int i=0 ; i<Atr->histLen ; i++) LBPUT(" %02x",atr[len+i]);
+ LBFLUSH();
+ LBPUT("%d: historical: '",id);
+ for(int i=0 ; i<Atr->histLen ; i++) LBPUT("%c",isprint(atr[len+i]) ? atr[len+i] : '.');
+ LBEND();
+
+ memcpy(Atr->hist,&atr[len],Atr->histLen);
+ len+=Atr->histLen;
+
+ // TCK
+ if(Atr->T!=0 && len+1<=Atr->atrLen) {
+ len++;
+ unsigned char cs=XorSum(atr+1,len-1);
+ // according to the ISO the initial TS byte isn't checksumed, but
+ // some cards do so. In this case the checksum is equal TS.
+ if(cs==0) PRINTF(L_CORE_SC,"%d: atr checksum ok",id);
+ else if(cs==atr[0]) PRINTF(L_CORE_SC,"iso: %d: atr checksum is TS (not ISO compliant)",id);
+ else {
+ PRINTF(L_CORE_SC,"%d: atr checksum FAILED (cs:%02x)",id,cs);
+ return false;
+ }
+ }
+ else PRINTF(L_CORE_SC,"%d: atr checksum not given/not required",id);
+
+ if(Atr->atrLen>len) PRINTF(L_CORE_SC,"%d: long atr read=%d len=%d",id,Atr->atrLen,len);
+ Atr->wwt=960*Atr->WI*Atr->F/(clock/1000);
+ Atr->bwt=(int)(960*(float)(1<<Atr->BWI)*372/(clock/1000));
+ PRINTF(L_CORE_SC,"%d: indicated wwt(T0)=%d ms, bwt(T1)=%d ms (at %.4f MHz)",id,Atr->wwt,Atr->bwt,(float)clock/1000000);
+ return true;
+}
+
+void cSmartCard::Invert(unsigned char *data, int n)
+{
+ static const unsigned char swaptab[] = { 15,7,11,3,13,5,9,1,14,6,10,2,12,4,8,0 };
+ for(int i=n-1; i>=0; i--)
+ data[i]=(swaptab[data[i]&0x0f]<<4) | swaptab[data[i]>>4];
+}
+
+int cSmartCard::SerRead(unsigned char *data, int len, int to)
+{
+ if(atr->T==1 || atr->T==14) {
+ int r=Read(data,len,to);
+ if(r<0) Test(false);
+ return r;
+ }
+ else {
+ PRINTF(L_CORE_SC,"%d: SerRead() not allowed for ISO compliant cards (T=%d)",id,atr->T);
+ return -1;
+ }
+}
+
+int cSmartCard::SerWrite(const unsigned char *data, int len)
+{
+ if(atr->T==1 || atr->T==14) {
+ int r=Write(data,len);
+ if(r<0) Test(false);
+ return r;
+ }
+ else {
+ PRINTF(L_CORE_SC,"%d: SerWrite() not allowed for ISO compliant cards (T=%d)",id,atr->T);
+ return -1;
+ }
+}
+
+int cSmartCard::Read(unsigned char *data, int len, int to)
+{
+ int r=ser->Read(data,len,cfg->serTO,to);
+ if(atr->convention==SM_INDIRECT && r>0) Invert(data,r);
+ return r;
+}
+
+int cSmartCard::Write(const unsigned char *data, int len)
+{
+ unsigned char tmp[len];
+ if(atr->convention==SM_INDIRECT) {
+ memcpy(tmp,data,len);
+ Invert(tmp,len);
+ data=tmp;
+ }
+ int r=ser->Write(data,len);
+ if(r>0) {
+ unsigned char buff[r];
+ int rr=ser->Read(buff,r,cfg->serTO);
+ if(rr<0) r=rr;
+ }
+ return r;
+}
+
+int cSmartCard::Procedure(unsigned char ins, int restLen)
+{
+ int r;
+ unsigned char buff;
+ LBSTARTF(L_CORE_SC);
+ LBPUT("%d: <- PROC: ",id);
+ do {
+ do {
+ if(Read(&buff,1,cfg->workTO)<=0) return -1;
+ LBPUT("%02x ",buff);
+ } while(buff==0x60);
+
+ if((buff&0xF0)==0x60 || (buff&0xF0)==0x90) { // SW1/SW2
+ sb[0]=buff;
+ if(Read(&buff,1)<=0) return -1;
+ LBPUT("%02x",buff);
+ sb[1]=buff;
+ return 0;
+ }
+ else {
+ if((buff&0xFE)==(ins&0xFE)) r=restLen;
+ else if((~buff&0xFE)==(ins&0xFE)) r=1;
+ else {
+ LBPUT("cannot handle procedure %02x (ins=%02x)\n",buff,ins);
+ return -1;
+ }
+ if(r>restLen) {
+ LBPUT("data overrun r=%d restLen=%d\n",r,restLen);
+ return -1;
+ }
+ }
+ } while(r==0);
+ LBEND();
+ return r;
+}
+
+bool cSmartCard::IsoRead(const unsigned char *cmd, unsigned char *data)
+{
+ if(atr->T==0) { // only for ISO complaint cards
+ LDUMP(L_CORE_SC,cmd,CMD_LEN,"%d: -> INS:",id);
+ if(Write(cmd,CMD_LEN)<0) return Test(false);
+ int tr=cmd[LEN_IDX] ? cmd[LEN_IDX] : 256;
+ int len=0;
+ while(1) {
+ int r=Procedure(cmd[INS_IDX],tr-len);
+ if(r<=0) return Test(r==0);
+ if(Read(data+len,r)<0) return Test(false);
+ LDUMP(L_CORE_SC,data+len,r,"%d: <- DATA:",id);
+ len+=r;
+ }
+ }
+ else PRINTF(L_CORE_SC,"%d: can't IsoRead() from incompatible card (T=%d)",id,atr->T);
+ return false;
+}
+
+bool cSmartCard::IsoWrite(const unsigned char *cmd, const unsigned char *data)
+{
+ if(atr->T==0) { // only for ISO complaint cards
+ LDUMP(L_CORE_SC,cmd,CMD_LEN,"%d: -> INS:",id);
+ if(Write(cmd,CMD_LEN)<0) return Test(false);
+ int len=0;
+ while(1) {
+ int r=Procedure(cmd[INS_IDX],cmd[LEN_IDX]-len);
+ if(r<=0) return Test(r==0);
+ if(Write(data+len,r)<0) return Test(false);
+ LDUMP(L_CORE_SC,data+len,r,"%d: -> DATA:",id);
+ len+=r;
+ }
+ }
+ else PRINTF(L_CORE_SC,"%d: can't IsoWrite() to incompatible card (T=%d)",id,atr->T);
+ return false;
+}
+
+bool cSmartCard::Test(bool res)
+{
+ if(!res) {
+ TriggerReset();
+ PRINTF(L_CORE_SC,"%d: reset triggered",id);
+ }
+ return res;
+}
+
+bool cSmartCard::Status(void)
+{
+ const struct StatusMsg *m=msg;
+ while(m->sb[0]!=0xFF) {
+ if(sb[0]==m->sb[0] && (m->sb[1]==0xFF || sb[1]==m->sb[1])) {
+ if(!m->retval) PRINTF(L_CORE_SC,"%d: %s (status: %02x %02x)",id,m->message,sb[0],sb[1]);
+ return m->retval;
+ }
+ m++;
+ }
+ PRINTF(L_CORE_SC,"%d: unknown (status: %02x %02x)",id,sb[0],sb[1]);
+ return false;
+}
+
+int cSmartCard::CheckSctLen(const unsigned char *data, int off)
+{
+ int l=SCT_LEN(data);
+ if(l+off > MAX_LEN) {
+ PRINTF(L_CORE_SC,"section too long %d > %d",l,MAX_LEN-off);
+ l=-1;
+ }
+ return l;
+}
+
+// -- cSmartCardData -----------------------------------------------------------
+
+cSmartCardData::cSmartCardData(int Ident)
+{
+ ident=Ident;
+}
+
+// -- cSmartCardLink -----------------------------------------------------------
+
+cSmartCardLink::cSmartCardLink(const char *Name, int Id)
+{
+ name=Name; id=Id;
+ cSmartCards::Register(this);
+}
+
+// -- cSmartCards --------------------------------------------------------------
+
+cSmartCardLink *cSmartCards::first=0;
+
+cSmartCards smartcards;
+
+static const char *serModes[] = { 0,"8e2","8o2","8n2" };
+
+cSmartCards::cSmartCards(void)
+:cThread("SmartcardWatcher")
+{
+ for(int i=0 ; i<MAX_PORTS ; i++) ports[i].Serial=0;
+ firstRun=true;
+}
+
+void cSmartCards::Register(cSmartCardLink *scl)
+{
+ PRINTF(L_CORE_DYN,"registering %s smartcard (id %x)",scl->name,scl->id);
+ scl->next=first;
+ first=scl;
+}
+
+void cSmartCards::LaunchWatcher(void)
+{
+ for(int i=0 ; i<MAX_PORTS ; i++)
+ if(ports[i].Serial) { Start(); return; }
+ firstRun=false;
+ PRINTF(L_GEN_WARN,"no smartcard interface defined!");
+}
+
+void cSmartCards::Shutdown(void)
+{
+ Cancel(3);
+ mutex.Lock();
+ for(int i=0 ; i<MAX_PORTS ; i++) {
+ if(ports[i].Serial) {
+ delete ports[i].Card;
+ delete ports[i].Serial;
+ ports[i].Serial=0;
+ }
+ }
+ dataList.Clear();
+ mutex.Unlock();
+}
+
+bool cSmartCards::AddPort(const char *devName, bool invCD, bool invRST, int clock)
+{
+ cMutexLock lock(&mutex);
+ for(int i=0 ; i<MAX_PORTS ; i++) {
+ if(!ports[i].Serial) {
+ cSerial *ser=new cSerial(devName,invCD,invRST);
+ if(ser && ser->Open()) {
+ ports[i].Card=0;
+ ports[i].CardId=0;
+ ports[i].UseCount=0;
+ ports[i].Dead=false;
+ ports[i].PortNum=i;
+ ports[i].Serial=ser;
+ ports[i].Clock=clock ? clock : ISO_FREQ;
+ PRINTF(L_CORE_LOAD,"smartcards: added serial port %s as port %d (%s CD, %s RESET, CLOCK %d)",
+ devName,i,invCD?"inverse":"normal",invRST?"inverse":"normal",ports[i].Clock);
+ return true;
+ }
+ PRINTF(L_GEN_ERROR,"failed to open serial port %s",devName);
+ delete ser;
+ return false;
+ }
+ else if(!strcmp(ports[i].Serial->DeviceName(),devName)) {
+ PRINTF(L_GEN_ERROR,"duplicate serial port %s. Check your config!",devName);
+ return false;
+ }
+ }
+ PRINTF(L_GEN_ERROR,"only %d serial ports supported",MAX_PORTS);
+ return false;
+}
+
+void cSmartCards::LoadData(const char *cfgdir)
+{
+ mutex.Lock();
+ dataList.Clear();
+ ConfRead("smartcard data",AddDirectory(cfgdir,DATAFILE));
+ mutex.Unlock();
+}
+
+bool cSmartCards::ParseLine(const char *line, bool fromCache)
+{
+ char *r=index(line,':');
+ if(!r) return false;
+ for(cSmartCardLink *scl=first; scl; scl=scl->next) {
+ if(!strncasecmp(scl->name,line,r-line)) {
+ cSmartCardData *scd=scl->CreateData();
+ if(scd && scd->Parse(r+1)) {
+ dataList.Add(scd);
+ break;
+ }
+ else {
+ delete scd;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+cSmartCardData *cSmartCards::FindCardData(cSmartCardData *param)
+{
+ cMutexLock lock(&mutex);
+ for(cSmartCardData *cd=dataList.First(); cd; cd=dataList.Next(cd))
+ if(cd->ident==param->ident && cd->Matches(param))
+ return cd;
+ return 0;
+}
+
+bool cSmartCards::HaveCard(int id)
+{
+ cMutexLock lock(&mutex);
+ while(Running() && firstRun) cond.Wait(mutex);
+ for(int i=0 ; i<MAX_PORTS ; i++)
+ if(ports[i].Serial && ports[i].CardId==id) return true;
+ return false;
+}
+
+cSmartCard *cSmartCards::LockCard(int id)
+{
+ mutex.Lock();
+ while(Running() && firstRun) cond.Wait(mutex);
+ for(int i=0 ; i<MAX_PORTS ; i++) {
+ if(ports[i].Serial && ports[i].CardId==id) {
+ ports[i].UseCount++;
+ mutex.Unlock();
+ cSmartCard *sc=ports[i].Card;
+ sc->Lock();
+ if(CardInserted(ports[i].Serial) && sc->CardUp() && !sc->NeedsReset())
+ return sc;
+ // if failed, unlock the card and decrement UseCount
+ sc->Unlock();
+ mutex.Lock();
+ ports[i].UseCount--;
+ cond.Broadcast();
+ break;
+ }
+ }
+ mutex.Unlock();
+ return 0;
+}
+
+void cSmartCards::ReleaseCard(cSmartCard *sc)
+{
+ sc->Unlock();
+ mutex.Lock();
+ for(int i=0 ; i<MAX_PORTS ; i++) {
+ if(ports[i].Card==sc) {
+ ports[i].UseCount--;
+ cond.Broadcast();
+ mutex.Unlock();
+ return;
+ }
+ }
+ mutex.Unlock();
+ PRINTF(L_GEN_DEBUG,"internal: can't find port in cSmartCards::ReleaseCard()");
+}
+
+bool cSmartCards::CardInserted(cSerial *ser)
+{
+ return ser->CheckCAR();
+}
+
+bool cSmartCards::CardReset(struct Port *port)
+{
+ if(Reset(port)<2 || !cSmartCard::ParseAtr(&port->Atr,port->PortNum,port->Clock))
+ return false;
+ if(!DoPTS(port)) {
+ // reset card again and continue without PTS
+ if(Reset(port)<2 || !cSmartCard::ParseAtr(&port->Atr,port->PortNum,port->Clock))
+ return false;
+ }
+ return true;
+}
+
+int cSmartCards::Reset(struct Port *port)
+{
+ cSerial *ser=port->Serial;
+ PRINTF(L_CORE_SC,"%d: reseting card (sermode %s)",port->PortNum,serModes[ser->CurrentMode()]);
+ ser->ToggleRTS();
+ cCondWait::SleepMs(20);
+ ser->ToggleRTS();
+ int r=ser->Read(port->Atr.atr,-MAX_ATR_LEN,800,2000);
+ port->Atr.atrLen=r;
+ if(r>0) LDUMP(L_CORE_SC,port->Atr.atr,r,"%d: <- ATR len=%d:",port->PortNum,r);
+ return r;
+}
+
+bool cSmartCards::DoPTS(struct Port *port)
+{
+ const struct Atr *atr=&port->Atr;
+ if(atr->F!=372 || atr->D!=1.0) { // PTS required
+ cSerial *ser=port->Serial;
+ const int id=port->PortNum;
+ int baud=(int)((float)port->Clock*atr->D/atr->F);
+ PRINTF(L_CORE_SC,"%d: PTS cycle: calculated baudrate %d",id,baud);
+
+ if(atr->Tspec<0) {
+ PRINTF(L_CORE_SC,"%d: negotiable mode",id);
+#ifdef NO_PTS_PROTO
+ PRINTF(L_CORE_SC,"%d: PTS disabled at compile time!!!",id);
+ return true;
+#else
+ unsigned char req[4], conf[16];
+ req[0]=0xFF;
+ req[1]=0x10 | atr->T;
+ req[2]=atr->TA1;
+ req[3]=XorSum(req,3);
+ LDUMP(L_CORE_SC,req,4,"%d: PTS request:",id);
+ if(ser->Write(req,4)!=4) {
+ PRINTF(L_CORE_SC,"%d: PTS request, write failed",id);
+ return false;
+ }
+ if(ser->Read(conf,4,100,100)!=4) {
+ PRINTF(L_CORE_SC,"%d: PTS request, echo readback failed",id);
+ return false;
+ }
+ int n=ser->Read(conf,-16,200,1000);
+ if(n>0) LDUMP(L_CORE_SC,conf,n,"%d: PTS confirm:",id);
+ if(n<1 || conf[0]!=0xFF || XorSum(conf,n)!=0) {
+ PRINTF(L_CORE_SC,"%d: PTS confirm, bad format",id);
+ return false;
+ }
+ if(n<4 || conf[1]!=req[1] || conf[2]!=req[2]) {
+ PRINTF(L_CORE_SC,"%d: PTS not confirmed",id);
+ return false;
+ }
+#endif // NO_PTS_PROTO
+ }
+ else
+ PRINTF(L_CORE_SC,"%d: specific mode Tspec=%d",id,atr->Tspec);
+
+ int mode=ser->CurrentMode();
+ if(atr->N==255) mode|=SM_1SB;
+ if(!ser->SetMode(mode,baud)) {
+ PRINTF(L_CORE_SC,"%d: setting baudrate %d failed",id,baud);
+ return false;
+ }
+ }
+ return true;
+}
+
+void cSmartCards::SetPort(struct Port *port, cSmartCard *sc, int id, bool dead)
+{
+ mutex.Lock();
+ while(port->UseCount) cond.Wait(mutex);
+ delete port->Card;
+ port->Card=sc;
+ port->CardId=id;
+ port->Dead=dead;
+ port->UseCount=0;
+ mutex.Unlock();
+}
+
+void cSmartCards::Action(void)
+{
+ while(Running()) {
+ for(int i=0 ; i<MAX_PORTS ;) {
+ struct Port *port=&ports[i];
+ cSerial *ser=port->Serial;
+ if(ser) {
+ if(port->Card) {
+ cSmartCard *sc=port->Card;
+ sc->Lock();
+ if(!CardInserted(ser)) {
+ sc->Unlock(); sc=0;
+ PRINTF(L_CORE_SC,"%d: card removed (UseCount=%d)",port->PortNum,port->UseCount);
+ SetPort(port,0,0,false);
+ }
+ else if(sc->NeedsReset()) {
+ PRINTF(L_CORE_SC,"%d: card reset requested",port->PortNum);
+ if(!ser->SetMode(ser->CurrentMode()&SM_MASK)
+ || !CardReset(port)
+ || !sc->Setup(ser,&port->Atr,port->PortNum)) {
+ sc->Unlock(); sc=0;
+ PRINTF(L_CORE_SC,"%d: card re-init failed",port->PortNum);
+ SetPort(port,0,0,true);
+ }
+ }
+ if(sc) sc->Unlock();
+ }
+ else if(CardInserted(ser)) {
+ if(!port->Dead) {
+ PRINTF(L_CORE_SC,"%d: new card inserted",port->PortNum);
+ for(int mode=SM_NONE+1 ; mode<SM_MAX ; mode++) {
+ if(ser->SetMode(mode)) {
+ if(CardReset(port)) {
+ cSmartCardLink *scl=first;
+ while(scl) {
+ PRINTF(L_CORE_SC,"%d: checking for %s card",port->PortNum,scl->name);
+ cSmartCard *sc=scl->Create();
+ if(sc && sc->Setup(ser,&port->Atr,port->PortNum)) {
+ SetPort(port,sc,scl->id,false);
+ goto next; // ugly, any better solution?
+ }
+ delete sc;
+ scl=scl->next;
+ }
+ PRINTF(L_CORE_SC,"%d: no card handler found",port->PortNum);
+ }
+ else PRINTF(L_CORE_SC,"%d: reset/atr error",port->PortNum);
+ }
+ else PRINTF(L_CORE_SC,"%d: failed to set serial mode %s",port->PortNum,serModes[mode]);
+ }
+ port->Dead=true;
+ PRINTF(L_CORE_SC,"%d: can't initialise new card, ignoring port until card reinserted",port->PortNum);
+ }
+ }
+ else {
+ if(port->Dead) PRINTF(L_CORE_SC,"%d: card removed, port reactivated",port->PortNum);
+ port->Dead=false;
+ }
+ }
+next: i++;
+ }
+ if(firstRun) {
+ mutex.Lock();
+ cond.Broadcast();
+ firstRun=false;
+ mutex.Unlock();
+ }
+ cCondWait::SleepMs(300);
+ }
+}
+
+bool cSmartCards::ListCard(int num, char *str, int len)
+{
+ if(num>=MAX_PORTS || !ports[num].Serial) return false;
+ str[0]=0;
+ mutex.Lock();
+ cSmartCard *sc=ports[num].Card;
+ if(sc) {
+ if(!sc->GetCardIdStr(str,len)) {
+ cSmartCardLink *scl=first;
+ while(scl) {
+ if(scl->id==ports[num].CardId) {
+ strn0cpy(str,scl->name,len);
+ break;
+ }
+ scl=scl->next;
+ }
+ }
+ }
+ mutex.Unlock();
+ return true;
+}
+
+bool cSmartCards::CardInfo(int num, char *str, int len)
+{
+ bool res=false;
+ if(num<MAX_PORTS && ports[num].Serial) {
+ str[0]=0;
+ mutex.Lock();
+ cSmartCard *sc=ports[num].Card;
+ if(sc) res=sc->GetCardInfoStr(str,len);
+ mutex.Unlock();
+ }
+ return res;
+}
+
+void cSmartCards::CardReset(int num)
+{
+ if(num<MAX_PORTS && ports[num].Serial) {
+ mutex.Lock();
+ cSmartCard *sc=ports[num].Card;
+ if(sc) sc->TriggerReset();
+ mutex.Unlock();
+ }
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SMARTCARD_H
+#define ___SMARTCARD_H
+
+#include <vdr/thread.h>
+#include "data.h"
+#include "misc.h"
+
+// ----------------------------------------------------------------
+
+#define MAX_LEN 256 // max. response length
+#define CMD_LEN 5 // command length
+#define INS_IDX 1 // INS index
+#define LEN_IDX 4 // LEN index
+
+#define SB_LEN 2 // status byte (SB) len
+#define MAX_ATR_LEN 33 // max. ATR length
+#define MAX_HIST 15 // max. number of historical characters
+#define IDSTR_LEN 25 // lenght of card identify string
+
+#define MAKE_SC_ID(a,b,c,d) (((a)<<24)+((b)<<16)+((c)<<8)+(d))
+
+// ----------------------------------------------------------------
+
+#define DEBUG_ISO // debug iso/smartcard functions
+#ifdef DEBUG_ISO
+#define di(x) { (x); }
+#else
+#define di(x) ;
+#endif
+
+#ifdef DEBUG_ISO
+#define DUMP(data,len) { const int l=(len); \
+ for(int i=0 ; i<l ; i++) printf(" %02x",*((data)+i)); \
+ printf("\n"); }
+#else
+#define DUMP(data,len) ;
+#endif
+
+// ----------------------------------------------------------------
+
+class cSerial;
+class cSmartCards;
+
+// ----------------------------------------------------------------
+
+class cInfoStr : public cLineBuff {
+private:
+ cMutex mutex;
+ char *current;
+public:
+ cInfoStr(void);
+ ~cInfoStr();
+ // Query API
+ bool Get(char *buff, int len);
+ // Construct API
+ void Begin(void);
+ void Finish();
+ };
+
+// ----------------------------------------------------------------
+
+#define SM_NONE 0
+#define SM_8E2 1
+#define SM_8O2 2
+#define SM_8N2 3
+#define SM_MAX 4
+#define SM_MASK 0x1F
+#define SM_1SB 0x80
+
+#define SM_DIRECT 0
+#define SM_INDIRECT 1
+
+struct CardConfig {
+ int SerMode;
+ int workTO, serTO; // values in ms
+ };
+
+struct StatusMsg {
+ unsigned char sb[SB_LEN];
+ char *message;
+ bool retval;
+ };
+
+struct Atr {
+ int T, F, N, WI, BWI, CWI, TA1, Tspec;
+ float D;
+ int wwt, bwt;
+ int atrLen, histLen;
+ int convention;
+ unsigned char atr[MAX_ATR_LEN];
+ unsigned char hist[MAX_HIST];
+ };
+
+class cSmartCard : protected cMutex {
+friend class cSmartCards;
+private:
+ cSerial *ser;
+ const struct CardConfig *cfg;
+ const struct StatusMsg *msg;
+ bool cardUp, needsReset;
+ int id;
+ //
+ int Procedure(unsigned char ins, int restLen);
+ int Read(unsigned char *data, int len, int to=0);
+ int Write(const unsigned char *data, int len);
+protected:
+ const struct Atr *atr;
+ unsigned char sb[SB_LEN];
+ char idStr[IDSTR_LEN];
+ cInfoStr infoStr;
+ //
+ int SerRead(unsigned char *data, int len, int to=0);
+ int SerWrite(const unsigned char *data, int len);
+ bool IsoRead(const unsigned char *cmd, unsigned char *data);
+ bool IsoWrite(const unsigned char *cmd, const unsigned char *data);
+ bool Status(void);
+ bool Test(bool res);
+ int CheckSctLen(const unsigned char *data, int off);
+ void TriggerReset(void) { needsReset=true; }
+ static void Invert(unsigned char *data, int n);
+public:
+ cSmartCard(const struct CardConfig *Cfg, const struct StatusMsg *Msg);
+ virtual ~cSmartCard() {};
+ virtual bool Init(void)=0;
+ virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)=0;
+ virtual bool Update(int pid, int caid, const unsigned char *data) { return false; }
+ virtual bool CanHandle(unsigned short CaId) { return true; }
+ //
+ bool Setup(cSerial *Ser, const struct Atr *Atr, int Id);
+ static bool ParseAtr(struct Atr *atr, int id, int clock);
+ bool CardUp(void) { return cardUp; }
+ bool NeedsReset(void) { return needsReset; }
+ bool GetCardIdStr(char *str, int len);
+ bool GetCardInfoStr(char *str, int len);
+ };
+
+// ----------------------------------------------------------------
+
+class cSmartCardData : public cSimpleItem {
+friend class cSmartCards;
+protected:
+ int ident;
+public:
+ cSmartCardData(int Ident);
+ virtual ~cSmartCardData() {}
+ virtual bool Parse(const char *line)=0;
+ virtual bool Matches(cSmartCardData *cmp)=0;
+ };
+
+// ----------------------------------------------------------------
+
+class cSmartCardLink {
+friend class cSmartCards;
+private:
+ cSmartCardLink *next;
+ const char *name;
+ int id;
+public:
+ cSmartCardLink(const char *Name, int Id);
+ virtual ~cSmartCardLink() {}
+ virtual cSmartCard *Create(void)=0;
+ virtual cSmartCardData *CreateData(void) { return 0; }
+ int ID(void) { return id; }
+ };
+
+// ----------------------------------------------------------------
+
+#define MAX_PORTS 4
+
+struct Port {
+ cSerial *Serial;
+ bool Dead;
+ cSmartCard *Card;
+ int CardId, UseCount, PortNum, Clock;
+ struct Atr Atr;
+ };
+
+class cSmartCards : private cThread, private cConfRead {
+friend class cSmartCardLink;
+private:
+ static cSmartCardLink *first;
+ cMutex mutex;
+ cCondVar cond;
+ struct Port ports[MAX_PORTS];
+ bool firstRun;
+ cSimpleList<cSmartCardData> dataList;
+ //
+ static void Register(cSmartCardLink *scl);
+ bool CardInserted(cSerial *ser);
+ bool CardReset(struct Port *port);
+ int Reset(struct Port *port);
+ bool DoPTS(struct Port *port);
+ void SetPort(struct Port *port, cSmartCard *sc, int id, bool dead);
+protected:
+ virtual void Action(void);
+ virtual bool ParseLine(const char *line, bool fromCache);
+public:
+ cSmartCards(void);
+ void Shutdown(void);
+ // to be called ONLY from a system class!
+ bool HaveCard(int id);
+ cSmartCard *LockCard(int id);
+ void ReleaseCard(cSmartCard *sc);
+ cSmartCardData *FindCardData(cSmartCardData *param);
+ // to be called ONLY from frontend thread!
+ bool AddPort(const char *devName, bool invCD, bool invRST, int clock);
+ void LaunchWatcher(void);
+ void LoadData(const char *cfgdir);
+ bool ListCard(int num, char *str, int len);
+ bool CardInfo(int num, char *str, int len);
+ void CardReset(int num);
+ };
+
+extern cSmartCards smartcards;
+
+#endif //___SMARTCARD_H
--- /dev/null
+#ifndef HEADER_AES_H\r
+#define HEADER_AES_H\r
+\r
+/*\r
+#ifdef OPENSSL_NO_AES\r
+#error AES is disabled.\r
+#endif\r
+*/\r
+\r
+#define AES_ENCRYPT 1\r
+#define AES_DECRYPT 0\r
+\r
+/* Because array size can't be a const in C, the following two are macros.\r
+ Both sizes are in bytes. */\r
+#define AES_MAXNR 14\r
+#define AES_BLOCK_SIZE 16\r
+\r
+#ifdef __cplusplus\r
+extern "C" {\r
+#endif\r
+\r
+#if defined(_MSC_VER) && !defined(OPENSSL_SYS_WINCE)\r
+# define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00)\r
+# define GETU32(p) SWAP(*((u32 *)(p)))\r
+# define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); }\r
+#else\r
+# define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ ((u32)(pt)[2] << 8) ^ ((u32)(pt)[3]))\r
+# define PUTU32(ct, st) { (ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); (ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); }\r
+#endif\r
+\r
+typedef unsigned long u32;\r
+typedef unsigned short u16;\r
+typedef unsigned char u8;\r
+\r
+#define MAXKC (256/32)\r
+#define MAXKB (256/8)\r
+#define MAXNR 14\r
+\r
+/* This controls loop-unrolling in aes_core.c */\r
+#undef FULL_UNROLL\r
+\r
+/* This should be a hidden type, but EVP requires that the size be known */\r
+struct aes_key_st {\r
+ unsigned long rd_key[4 *(AES_MAXNR + 1)];\r
+ int rounds;\r
+};\r
+typedef struct aes_key_st AES_KEY;\r
+\r
+const char *AES_options(void);\r
+\r
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,\r
+ AES_KEY *key);\r
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,\r
+ AES_KEY *key);\r
+\r
+void AES_encrypt(const unsigned char *in, unsigned char *out,\r
+ const AES_KEY *key);\r
+void AES_decrypt(const unsigned char *in, unsigned char *out,\r
+ const AES_KEY *key);\r
+\r
+void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,\r
+ const AES_KEY *key, const int enc);\r
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,\r
+ const unsigned long length, const AES_KEY *key,\r
+ unsigned char *ivec, const int enc);\r
+void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,\r
+ const unsigned long length, const AES_KEY *key,\r
+ unsigned char *ivec, int *num, const int enc);\r
+void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out,\r
+ const unsigned long length, const AES_KEY *key,\r
+ unsigned char *ivec, int *num);\r
+void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out,\r
+ const unsigned long length, const AES_KEY *key,\r
+ unsigned char ivec[AES_BLOCK_SIZE],\r
+ unsigned char ecount_buf[AES_BLOCK_SIZE],\r
+ unsigned int *num);\r
+\r
+\r
+#ifdef __cplusplus\r
+}\r
+#endif\r
+\r
+#endif /* !HEADER_AES_H */\r
--- /dev/null
+#include <assert.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include "aes.h"\r
+\r
+static const u32 Te0[256] = {\r
+ 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU,\r
+ 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U,\r
+ 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU,\r
+ 0xe7fefe19U, 0xb5d7d762U, 0x4dababe6U, 0xec76769aU,\r
+ 0x8fcaca45U, 0x1f82829dU, 0x89c9c940U, 0xfa7d7d87U,\r
+ 0xeffafa15U, 0xb25959ebU, 0x8e4747c9U, 0xfbf0f00bU,\r
+ 0x41adadecU, 0xb3d4d467U, 0x5fa2a2fdU, 0x45afafeaU,\r
+ 0x239c9cbfU, 0x53a4a4f7U, 0xe4727296U, 0x9bc0c05bU,\r
+ 0x75b7b7c2U, 0xe1fdfd1cU, 0x3d9393aeU, 0x4c26266aU,\r
+ 0x6c36365aU, 0x7e3f3f41U, 0xf5f7f702U, 0x83cccc4fU,\r
+ 0x6834345cU, 0x51a5a5f4U, 0xd1e5e534U, 0xf9f1f108U,\r
+ 0xe2717193U, 0xabd8d873U, 0x62313153U, 0x2a15153fU,\r
+ 0x0804040cU, 0x95c7c752U, 0x46232365U, 0x9dc3c35eU,\r
+ 0x30181828U, 0x379696a1U, 0x0a05050fU, 0x2f9a9ab5U,\r
+ 0x0e070709U, 0x24121236U, 0x1b80809bU, 0xdfe2e23dU,\r
+ 0xcdebeb26U, 0x4e272769U, 0x7fb2b2cdU, 0xea75759fU,\r
+ 0x1209091bU, 0x1d83839eU, 0x582c2c74U, 0x341a1a2eU,\r
+ 0x361b1b2dU, 0xdc6e6eb2U, 0xb45a5aeeU, 0x5ba0a0fbU,\r
+ 0xa45252f6U, 0x763b3b4dU, 0xb7d6d661U, 0x7db3b3ceU,\r
+ 0x5229297bU, 0xdde3e33eU, 0x5e2f2f71U, 0x13848497U,\r
+ 0xa65353f5U, 0xb9d1d168U, 0x00000000U, 0xc1eded2cU,\r
+ 0x40202060U, 0xe3fcfc1fU, 0x79b1b1c8U, 0xb65b5bedU,\r
+ 0xd46a6abeU, 0x8dcbcb46U, 0x67bebed9U, 0x7239394bU,\r
+ 0x944a4adeU, 0x984c4cd4U, 0xb05858e8U, 0x85cfcf4aU,\r
+ 0xbbd0d06bU, 0xc5efef2aU, 0x4faaaae5U, 0xedfbfb16U,\r
+ 0x864343c5U, 0x9a4d4dd7U, 0x66333355U, 0x11858594U,\r
+ 0x8a4545cfU, 0xe9f9f910U, 0x04020206U, 0xfe7f7f81U,\r
+ 0xa05050f0U, 0x783c3c44U, 0x259f9fbaU, 0x4ba8a8e3U,\r
+ 0xa25151f3U, 0x5da3a3feU, 0x804040c0U, 0x058f8f8aU,\r
+ 0x3f9292adU, 0x219d9dbcU, 0x70383848U, 0xf1f5f504U,\r
+ 0x63bcbcdfU, 0x77b6b6c1U, 0xafdada75U, 0x42212163U,\r
+ 0x20101030U, 0xe5ffff1aU, 0xfdf3f30eU, 0xbfd2d26dU,\r
+ 0x81cdcd4cU, 0x180c0c14U, 0x26131335U, 0xc3ecec2fU,\r
+ 0xbe5f5fe1U, 0x359797a2U, 0x884444ccU, 0x2e171739U,\r
+ 0x93c4c457U, 0x55a7a7f2U, 0xfc7e7e82U, 0x7a3d3d47U,\r
+ 0xc86464acU, 0xba5d5de7U, 0x3219192bU, 0xe6737395U,\r
+ 0xc06060a0U, 0x19818198U, 0x9e4f4fd1U, 0xa3dcdc7fU,\r
+ 0x44222266U, 0x542a2a7eU, 0x3b9090abU, 0x0b888883U,\r
+ 0x8c4646caU, 0xc7eeee29U, 0x6bb8b8d3U, 0x2814143cU,\r
+ 0xa7dede79U, 0xbc5e5ee2U, 0x160b0b1dU, 0xaddbdb76U,\r
+ 0xdbe0e03bU, 0x64323256U, 0x743a3a4eU, 0x140a0a1eU,\r
+ 0x924949dbU, 0x0c06060aU, 0x4824246cU, 0xb85c5ce4U,\r
+ 0x9fc2c25dU, 0xbdd3d36eU, 0x43acacefU, 0xc46262a6U,\r
+ 0x399191a8U, 0x319595a4U, 0xd3e4e437U, 0xf279798bU,\r
+ 0xd5e7e732U, 0x8bc8c843U, 0x6e373759U, 0xda6d6db7U,\r
+ 0x018d8d8cU, 0xb1d5d564U, 0x9c4e4ed2U, 0x49a9a9e0U,\r
+ 0xd86c6cb4U, 0xac5656faU, 0xf3f4f407U, 0xcfeaea25U,\r
+ 0xca6565afU, 0xf47a7a8eU, 0x47aeaee9U, 0x10080818U,\r
+ 0x6fbabad5U, 0xf0787888U, 0x4a25256fU, 0x5c2e2e72U,\r
+ 0x381c1c24U, 0x57a6a6f1U, 0x73b4b4c7U, 0x97c6c651U,\r
+ 0xcbe8e823U, 0xa1dddd7cU, 0xe874749cU, 0x3e1f1f21U,\r
+ 0x964b4bddU, 0x61bdbddcU, 0x0d8b8b86U, 0x0f8a8a85U,\r
+ 0xe0707090U, 0x7c3e3e42U, 0x71b5b5c4U, 0xcc6666aaU,\r
+ 0x904848d8U, 0x06030305U, 0xf7f6f601U, 0x1c0e0e12U,\r
+ 0xc26161a3U, 0x6a35355fU, 0xae5757f9U, 0x69b9b9d0U,\r
+ 0x17868691U, 0x99c1c158U, 0x3a1d1d27U, 0x279e9eb9U,\r
+ 0xd9e1e138U, 0xebf8f813U, 0x2b9898b3U, 0x22111133U,\r
+ 0xd26969bbU, 0xa9d9d970U, 0x078e8e89U, 0x339494a7U,\r
+ 0x2d9b9bb6U, 0x3c1e1e22U, 0x15878792U, 0xc9e9e920U,\r
+ 0x87cece49U, 0xaa5555ffU, 0x50282878U, 0xa5dfdf7aU,\r
+ 0x038c8c8fU, 0x59a1a1f8U, 0x09898980U, 0x1a0d0d17U,\r
+ 0x65bfbfdaU, 0xd7e6e631U, 0x844242c6U, 0xd06868b8U,\r
+ 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U,\r
+ 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU,\r
+};\r
+static const u32 Te1[256] = {\r
+ 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU,\r
+ 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U,\r
+ 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU,\r
+ 0x19e7fefeU, 0x62b5d7d7U, 0xe64dababU, 0x9aec7676U,\r
+ 0x458fcacaU, 0x9d1f8282U, 0x4089c9c9U, 0x87fa7d7dU,\r
+ 0x15effafaU, 0xebb25959U, 0xc98e4747U, 0x0bfbf0f0U,\r
+ 0xec41adadU, 0x67b3d4d4U, 0xfd5fa2a2U, 0xea45afafU,\r
+ 0xbf239c9cU, 0xf753a4a4U, 0x96e47272U, 0x5b9bc0c0U,\r
+ 0xc275b7b7U, 0x1ce1fdfdU, 0xae3d9393U, 0x6a4c2626U,\r
+ 0x5a6c3636U, 0x417e3f3fU, 0x02f5f7f7U, 0x4f83ccccU,\r
+ 0x5c683434U, 0xf451a5a5U, 0x34d1e5e5U, 0x08f9f1f1U,\r
+ 0x93e27171U, 0x73abd8d8U, 0x53623131U, 0x3f2a1515U,\r
+ 0x0c080404U, 0x5295c7c7U, 0x65462323U, 0x5e9dc3c3U,\r
+ 0x28301818U, 0xa1379696U, 0x0f0a0505U, 0xb52f9a9aU,\r
+ 0x090e0707U, 0x36241212U, 0x9b1b8080U, 0x3ddfe2e2U,\r
+ 0x26cdebebU, 0x694e2727U, 0xcd7fb2b2U, 0x9fea7575U,\r
+ 0x1b120909U, 0x9e1d8383U, 0x74582c2cU, 0x2e341a1aU,\r
+ 0x2d361b1bU, 0xb2dc6e6eU, 0xeeb45a5aU, 0xfb5ba0a0U,\r
+ 0xf6a45252U, 0x4d763b3bU, 0x61b7d6d6U, 0xce7db3b3U,\r
+ 0x7b522929U, 0x3edde3e3U, 0x715e2f2fU, 0x97138484U,\r
+ 0xf5a65353U, 0x68b9d1d1U, 0x00000000U, 0x2cc1ededU,\r
+ 0x60402020U, 0x1fe3fcfcU, 0xc879b1b1U, 0xedb65b5bU,\r
+ 0xbed46a6aU, 0x468dcbcbU, 0xd967bebeU, 0x4b723939U,\r
+ 0xde944a4aU, 0xd4984c4cU, 0xe8b05858U, 0x4a85cfcfU,\r
+ 0x6bbbd0d0U, 0x2ac5efefU, 0xe54faaaaU, 0x16edfbfbU,\r
+ 0xc5864343U, 0xd79a4d4dU, 0x55663333U, 0x94118585U,\r
+ 0xcf8a4545U, 0x10e9f9f9U, 0x06040202U, 0x81fe7f7fU,\r
+ 0xf0a05050U, 0x44783c3cU, 0xba259f9fU, 0xe34ba8a8U,\r
+ 0xf3a25151U, 0xfe5da3a3U, 0xc0804040U, 0x8a058f8fU,\r
+ 0xad3f9292U, 0xbc219d9dU, 0x48703838U, 0x04f1f5f5U,\r
+ 0xdf63bcbcU, 0xc177b6b6U, 0x75afdadaU, 0x63422121U,\r
+ 0x30201010U, 0x1ae5ffffU, 0x0efdf3f3U, 0x6dbfd2d2U,\r
+ 0x4c81cdcdU, 0x14180c0cU, 0x35261313U, 0x2fc3ececU,\r
+ 0xe1be5f5fU, 0xa2359797U, 0xcc884444U, 0x392e1717U,\r
+ 0x5793c4c4U, 0xf255a7a7U, 0x82fc7e7eU, 0x477a3d3dU,\r
+ 0xacc86464U, 0xe7ba5d5dU, 0x2b321919U, 0x95e67373U,\r
+ 0xa0c06060U, 0x98198181U, 0xd19e4f4fU, 0x7fa3dcdcU,\r
+ 0x66442222U, 0x7e542a2aU, 0xab3b9090U, 0x830b8888U,\r
+ 0xca8c4646U, 0x29c7eeeeU, 0xd36bb8b8U, 0x3c281414U,\r
+ 0x79a7dedeU, 0xe2bc5e5eU, 0x1d160b0bU, 0x76addbdbU,\r
+ 0x3bdbe0e0U, 0x56643232U, 0x4e743a3aU, 0x1e140a0aU,\r
+ 0xdb924949U, 0x0a0c0606U, 0x6c482424U, 0xe4b85c5cU,\r
+ 0x5d9fc2c2U, 0x6ebdd3d3U, 0xef43acacU, 0xa6c46262U,\r
+ 0xa8399191U, 0xa4319595U, 0x37d3e4e4U, 0x8bf27979U,\r
+ 0x32d5e7e7U, 0x438bc8c8U, 0x596e3737U, 0xb7da6d6dU,\r
+ 0x8c018d8dU, 0x64b1d5d5U, 0xd29c4e4eU, 0xe049a9a9U,\r
+ 0xb4d86c6cU, 0xfaac5656U, 0x07f3f4f4U, 0x25cfeaeaU,\r
+ 0xafca6565U, 0x8ef47a7aU, 0xe947aeaeU, 0x18100808U,\r
+ 0xd56fbabaU, 0x88f07878U, 0x6f4a2525U, 0x725c2e2eU,\r
+ 0x24381c1cU, 0xf157a6a6U, 0xc773b4b4U, 0x5197c6c6U,\r
+ 0x23cbe8e8U, 0x7ca1ddddU, 0x9ce87474U, 0x213e1f1fU,\r
+ 0xdd964b4bU, 0xdc61bdbdU, 0x860d8b8bU, 0x850f8a8aU,\r
+ 0x90e07070U, 0x427c3e3eU, 0xc471b5b5U, 0xaacc6666U,\r
+ 0xd8904848U, 0x05060303U, 0x01f7f6f6U, 0x121c0e0eU,\r
+ 0xa3c26161U, 0x5f6a3535U, 0xf9ae5757U, 0xd069b9b9U,\r
+ 0x91178686U, 0x5899c1c1U, 0x273a1d1dU, 0xb9279e9eU,\r
+ 0x38d9e1e1U, 0x13ebf8f8U, 0xb32b9898U, 0x33221111U,\r
+ 0xbbd26969U, 0x70a9d9d9U, 0x89078e8eU, 0xa7339494U,\r
+ 0xb62d9b9bU, 0x223c1e1eU, 0x92158787U, 0x20c9e9e9U,\r
+ 0x4987ceceU, 0xffaa5555U, 0x78502828U, 0x7aa5dfdfU,\r
+ 0x8f038c8cU, 0xf859a1a1U, 0x80098989U, 0x171a0d0dU,\r
+ 0xda65bfbfU, 0x31d7e6e6U, 0xc6844242U, 0xb8d06868U,\r
+ 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU,\r
+ 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U,\r
+};\r
+static const u32 Te2[256] = {\r
+ 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU,\r
+ 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U,\r
+ 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU,\r
+ 0xfe19e7feU, 0xd762b5d7U, 0xabe64dabU, 0x769aec76U,\r
+ 0xca458fcaU, 0x829d1f82U, 0xc94089c9U, 0x7d87fa7dU,\r
+ 0xfa15effaU, 0x59ebb259U, 0x47c98e47U, 0xf00bfbf0U,\r
+ 0xadec41adU, 0xd467b3d4U, 0xa2fd5fa2U, 0xafea45afU,\r
+ 0x9cbf239cU, 0xa4f753a4U, 0x7296e472U, 0xc05b9bc0U,\r
+ 0xb7c275b7U, 0xfd1ce1fdU, 0x93ae3d93U, 0x266a4c26U,\r
+ 0x365a6c36U, 0x3f417e3fU, 0xf702f5f7U, 0xcc4f83ccU,\r
+ 0x345c6834U, 0xa5f451a5U, 0xe534d1e5U, 0xf108f9f1U,\r
+ 0x7193e271U, 0xd873abd8U, 0x31536231U, 0x153f2a15U,\r
+ 0x040c0804U, 0xc75295c7U, 0x23654623U, 0xc35e9dc3U,\r
+ 0x18283018U, 0x96a13796U, 0x050f0a05U, 0x9ab52f9aU,\r
+ 0x07090e07U, 0x12362412U, 0x809b1b80U, 0xe23ddfe2U,\r
+ 0xeb26cdebU, 0x27694e27U, 0xb2cd7fb2U, 0x759fea75U,\r
+ 0x091b1209U, 0x839e1d83U, 0x2c74582cU, 0x1a2e341aU,\r
+ 0x1b2d361bU, 0x6eb2dc6eU, 0x5aeeb45aU, 0xa0fb5ba0U,\r
+ 0x52f6a452U, 0x3b4d763bU, 0xd661b7d6U, 0xb3ce7db3U,\r
+ 0x297b5229U, 0xe33edde3U, 0x2f715e2fU, 0x84971384U,\r
+ 0x53f5a653U, 0xd168b9d1U, 0x00000000U, 0xed2cc1edU,\r
+ 0x20604020U, 0xfc1fe3fcU, 0xb1c879b1U, 0x5bedb65bU,\r
+ 0x6abed46aU, 0xcb468dcbU, 0xbed967beU, 0x394b7239U,\r
+ 0x4ade944aU, 0x4cd4984cU, 0x58e8b058U, 0xcf4a85cfU,\r
+ 0xd06bbbd0U, 0xef2ac5efU, 0xaae54faaU, 0xfb16edfbU,\r
+ 0x43c58643U, 0x4dd79a4dU, 0x33556633U, 0x85941185U,\r
+ 0x45cf8a45U, 0xf910e9f9U, 0x02060402U, 0x7f81fe7fU,\r
+ 0x50f0a050U, 0x3c44783cU, 0x9fba259fU, 0xa8e34ba8U,\r
+ 0x51f3a251U, 0xa3fe5da3U, 0x40c08040U, 0x8f8a058fU,\r
+ 0x92ad3f92U, 0x9dbc219dU, 0x38487038U, 0xf504f1f5U,\r
+ 0xbcdf63bcU, 0xb6c177b6U, 0xda75afdaU, 0x21634221U,\r
+ 0x10302010U, 0xff1ae5ffU, 0xf30efdf3U, 0xd26dbfd2U,\r
+ 0xcd4c81cdU, 0x0c14180cU, 0x13352613U, 0xec2fc3ecU,\r
+ 0x5fe1be5fU, 0x97a23597U, 0x44cc8844U, 0x17392e17U,\r
+ 0xc45793c4U, 0xa7f255a7U, 0x7e82fc7eU, 0x3d477a3dU,\r
+ 0x64acc864U, 0x5de7ba5dU, 0x192b3219U, 0x7395e673U,\r
+ 0x60a0c060U, 0x81981981U, 0x4fd19e4fU, 0xdc7fa3dcU,\r
+ 0x22664422U, 0x2a7e542aU, 0x90ab3b90U, 0x88830b88U,\r
+ 0x46ca8c46U, 0xee29c7eeU, 0xb8d36bb8U, 0x143c2814U,\r
+ 0xde79a7deU, 0x5ee2bc5eU, 0x0b1d160bU, 0xdb76addbU,\r
+ 0xe03bdbe0U, 0x32566432U, 0x3a4e743aU, 0x0a1e140aU,\r
+ 0x49db9249U, 0x060a0c06U, 0x246c4824U, 0x5ce4b85cU,\r
+ 0xc25d9fc2U, 0xd36ebdd3U, 0xacef43acU, 0x62a6c462U,\r
+ 0x91a83991U, 0x95a43195U, 0xe437d3e4U, 0x798bf279U,\r
+ 0xe732d5e7U, 0xc8438bc8U, 0x37596e37U, 0x6db7da6dU,\r
+ 0x8d8c018dU, 0xd564b1d5U, 0x4ed29c4eU, 0xa9e049a9U,\r
+ 0x6cb4d86cU, 0x56faac56U, 0xf407f3f4U, 0xea25cfeaU,\r
+ 0x65afca65U, 0x7a8ef47aU, 0xaee947aeU, 0x08181008U,\r
+ 0xbad56fbaU, 0x7888f078U, 0x256f4a25U, 0x2e725c2eU,\r
+ 0x1c24381cU, 0xa6f157a6U, 0xb4c773b4U, 0xc65197c6U,\r
+ 0xe823cbe8U, 0xdd7ca1ddU, 0x749ce874U, 0x1f213e1fU,\r
+ 0x4bdd964bU, 0xbddc61bdU, 0x8b860d8bU, 0x8a850f8aU,\r
+ 0x7090e070U, 0x3e427c3eU, 0xb5c471b5U, 0x66aacc66U,\r
+ 0x48d89048U, 0x03050603U, 0xf601f7f6U, 0x0e121c0eU,\r
+ 0x61a3c261U, 0x355f6a35U, 0x57f9ae57U, 0xb9d069b9U,\r
+ 0x86911786U, 0xc15899c1U, 0x1d273a1dU, 0x9eb9279eU,\r
+ 0xe138d9e1U, 0xf813ebf8U, 0x98b32b98U, 0x11332211U,\r
+ 0x69bbd269U, 0xd970a9d9U, 0x8e89078eU, 0x94a73394U,\r
+ 0x9bb62d9bU, 0x1e223c1eU, 0x87921587U, 0xe920c9e9U,\r
+ 0xce4987ceU, 0x55ffaa55U, 0x28785028U, 0xdf7aa5dfU,\r
+ 0x8c8f038cU, 0xa1f859a1U, 0x89800989U, 0x0d171a0dU,\r
+ 0xbfda65bfU, 0xe631d7e6U, 0x42c68442U, 0x68b8d068U,\r
+ 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU,\r
+ 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U,\r
+};\r
+static const u32 Te3[256] = {\r
+\r
+ 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U,\r
+ 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U,\r
+ 0x30305060U, 0x01010302U, 0x6767a9ceU, 0x2b2b7d56U,\r
+ 0xfefe19e7U, 0xd7d762b5U, 0xababe64dU, 0x76769aecU,\r
+ 0xcaca458fU, 0x82829d1fU, 0xc9c94089U, 0x7d7d87faU,\r
+ 0xfafa15efU, 0x5959ebb2U, 0x4747c98eU, 0xf0f00bfbU,\r
+ 0xadadec41U, 0xd4d467b3U, 0xa2a2fd5fU, 0xafafea45U,\r
+ 0x9c9cbf23U, 0xa4a4f753U, 0x727296e4U, 0xc0c05b9bU,\r
+ 0xb7b7c275U, 0xfdfd1ce1U, 0x9393ae3dU, 0x26266a4cU,\r
+ 0x36365a6cU, 0x3f3f417eU, 0xf7f702f5U, 0xcccc4f83U,\r
+ 0x34345c68U, 0xa5a5f451U, 0xe5e534d1U, 0xf1f108f9U,\r
+ 0x717193e2U, 0xd8d873abU, 0x31315362U, 0x15153f2aU,\r
+ 0x04040c08U, 0xc7c75295U, 0x23236546U, 0xc3c35e9dU,\r
+ 0x18182830U, 0x9696a137U, 0x05050f0aU, 0x9a9ab52fU,\r
+ 0x0707090eU, 0x12123624U, 0x80809b1bU, 0xe2e23ddfU,\r
+ 0xebeb26cdU, 0x2727694eU, 0xb2b2cd7fU, 0x75759feaU,\r
+ 0x09091b12U, 0x83839e1dU, 0x2c2c7458U, 0x1a1a2e34U,\r
+ 0x1b1b2d36U, 0x6e6eb2dcU, 0x5a5aeeb4U, 0xa0a0fb5bU,\r
+ 0x5252f6a4U, 0x3b3b4d76U, 0xd6d661b7U, 0xb3b3ce7dU,\r
+ 0x29297b52U, 0xe3e33eddU, 0x2f2f715eU, 0x84849713U,\r
+ 0x5353f5a6U, 0xd1d168b9U, 0x00000000U, 0xeded2cc1U,\r
+ 0x20206040U, 0xfcfc1fe3U, 0xb1b1c879U, 0x5b5bedb6U,\r
+ 0x6a6abed4U, 0xcbcb468dU, 0xbebed967U, 0x39394b72U,\r
+ 0x4a4ade94U, 0x4c4cd498U, 0x5858e8b0U, 0xcfcf4a85U,\r
+ 0xd0d06bbbU, 0xefef2ac5U, 0xaaaae54fU, 0xfbfb16edU,\r
+ 0x4343c586U, 0x4d4dd79aU, 0x33335566U, 0x85859411U,\r
+ 0x4545cf8aU, 0xf9f910e9U, 0x02020604U, 0x7f7f81feU,\r
+ 0x5050f0a0U, 0x3c3c4478U, 0x9f9fba25U, 0xa8a8e34bU,\r
+ 0x5151f3a2U, 0xa3a3fe5dU, 0x4040c080U, 0x8f8f8a05U,\r
+ 0x9292ad3fU, 0x9d9dbc21U, 0x38384870U, 0xf5f504f1U,\r
+ 0xbcbcdf63U, 0xb6b6c177U, 0xdada75afU, 0x21216342U,\r
+ 0x10103020U, 0xffff1ae5U, 0xf3f30efdU, 0xd2d26dbfU,\r
+ 0xcdcd4c81U, 0x0c0c1418U, 0x13133526U, 0xecec2fc3U,\r
+ 0x5f5fe1beU, 0x9797a235U, 0x4444cc88U, 0x1717392eU,\r
+ 0xc4c45793U, 0xa7a7f255U, 0x7e7e82fcU, 0x3d3d477aU,\r
+ 0x6464acc8U, 0x5d5de7baU, 0x19192b32U, 0x737395e6U,\r
+ 0x6060a0c0U, 0x81819819U, 0x4f4fd19eU, 0xdcdc7fa3U,\r
+ 0x22226644U, 0x2a2a7e54U, 0x9090ab3bU, 0x8888830bU,\r
+ 0x4646ca8cU, 0xeeee29c7U, 0xb8b8d36bU, 0x14143c28U,\r
+ 0xdede79a7U, 0x5e5ee2bcU, 0x0b0b1d16U, 0xdbdb76adU,\r
+ 0xe0e03bdbU, 0x32325664U, 0x3a3a4e74U, 0x0a0a1e14U,\r
+ 0x4949db92U, 0x06060a0cU, 0x24246c48U, 0x5c5ce4b8U,\r
+ 0xc2c25d9fU, 0xd3d36ebdU, 0xacacef43U, 0x6262a6c4U,\r
+ 0x9191a839U, 0x9595a431U, 0xe4e437d3U, 0x79798bf2U,\r
+ 0xe7e732d5U, 0xc8c8438bU, 0x3737596eU, 0x6d6db7daU,\r
+ 0x8d8d8c01U, 0xd5d564b1U, 0x4e4ed29cU, 0xa9a9e049U,\r
+ 0x6c6cb4d8U, 0x5656faacU, 0xf4f407f3U, 0xeaea25cfU,\r
+ 0x6565afcaU, 0x7a7a8ef4U, 0xaeaee947U, 0x08081810U,\r
+ 0xbabad56fU, 0x787888f0U, 0x25256f4aU, 0x2e2e725cU,\r
+ 0x1c1c2438U, 0xa6a6f157U, 0xb4b4c773U, 0xc6c65197U,\r
+ 0xe8e823cbU, 0xdddd7ca1U, 0x74749ce8U, 0x1f1f213eU,\r
+ 0x4b4bdd96U, 0xbdbddc61U, 0x8b8b860dU, 0x8a8a850fU,\r
+ 0x707090e0U, 0x3e3e427cU, 0xb5b5c471U, 0x6666aaccU,\r
+ 0x4848d890U, 0x03030506U, 0xf6f601f7U, 0x0e0e121cU,\r
+ 0x6161a3c2U, 0x35355f6aU, 0x5757f9aeU, 0xb9b9d069U,\r
+ 0x86869117U, 0xc1c15899U, 0x1d1d273aU, 0x9e9eb927U,\r
+ 0xe1e138d9U, 0xf8f813ebU, 0x9898b32bU, 0x11113322U,\r
+ 0x6969bbd2U, 0xd9d970a9U, 0x8e8e8907U, 0x9494a733U,\r
+ 0x9b9bb62dU, 0x1e1e223cU, 0x87879215U, 0xe9e920c9U,\r
+ 0xcece4987U, 0x5555ffaaU, 0x28287850U, 0xdfdf7aa5U,\r
+ 0x8c8c8f03U, 0xa1a1f859U, 0x89898009U, 0x0d0d171aU,\r
+ 0xbfbfda65U, 0xe6e631d7U, 0x4242c684U, 0x6868b8d0U,\r
+ 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU,\r
+ 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU,\r
+};\r
+static const u32 Te4[256] = {\r
+ 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU,\r
+ 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U,\r
+ 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU,\r
+ 0xfefefefeU, 0xd7d7d7d7U, 0xababababU, 0x76767676U,\r
+ 0xcacacacaU, 0x82828282U, 0xc9c9c9c9U, 0x7d7d7d7dU,\r
+ 0xfafafafaU, 0x59595959U, 0x47474747U, 0xf0f0f0f0U,\r
+ 0xadadadadU, 0xd4d4d4d4U, 0xa2a2a2a2U, 0xafafafafU,\r
+ 0x9c9c9c9cU, 0xa4a4a4a4U, 0x72727272U, 0xc0c0c0c0U,\r
+ 0xb7b7b7b7U, 0xfdfdfdfdU, 0x93939393U, 0x26262626U,\r
+ 0x36363636U, 0x3f3f3f3fU, 0xf7f7f7f7U, 0xccccccccU,\r
+ 0x34343434U, 0xa5a5a5a5U, 0xe5e5e5e5U, 0xf1f1f1f1U,\r
+ 0x71717171U, 0xd8d8d8d8U, 0x31313131U, 0x15151515U,\r
+ 0x04040404U, 0xc7c7c7c7U, 0x23232323U, 0xc3c3c3c3U,\r
+ 0x18181818U, 0x96969696U, 0x05050505U, 0x9a9a9a9aU,\r
+ 0x07070707U, 0x12121212U, 0x80808080U, 0xe2e2e2e2U,\r
+ 0xebebebebU, 0x27272727U, 0xb2b2b2b2U, 0x75757575U,\r
+ 0x09090909U, 0x83838383U, 0x2c2c2c2cU, 0x1a1a1a1aU,\r
+ 0x1b1b1b1bU, 0x6e6e6e6eU, 0x5a5a5a5aU, 0xa0a0a0a0U,\r
+ 0x52525252U, 0x3b3b3b3bU, 0xd6d6d6d6U, 0xb3b3b3b3U,\r
+ 0x29292929U, 0xe3e3e3e3U, 0x2f2f2f2fU, 0x84848484U,\r
+ 0x53535353U, 0xd1d1d1d1U, 0x00000000U, 0xededededU,\r
+ 0x20202020U, 0xfcfcfcfcU, 0xb1b1b1b1U, 0x5b5b5b5bU,\r
+ 0x6a6a6a6aU, 0xcbcbcbcbU, 0xbebebebeU, 0x39393939U,\r
+ 0x4a4a4a4aU, 0x4c4c4c4cU, 0x58585858U, 0xcfcfcfcfU,\r
+ 0xd0d0d0d0U, 0xefefefefU, 0xaaaaaaaaU, 0xfbfbfbfbU,\r
+ 0x43434343U, 0x4d4d4d4dU, 0x33333333U, 0x85858585U,\r
+ 0x45454545U, 0xf9f9f9f9U, 0x02020202U, 0x7f7f7f7fU,\r
+ 0x50505050U, 0x3c3c3c3cU, 0x9f9f9f9fU, 0xa8a8a8a8U,\r
+ 0x51515151U, 0xa3a3a3a3U, 0x40404040U, 0x8f8f8f8fU,\r
+ 0x92929292U, 0x9d9d9d9dU, 0x38383838U, 0xf5f5f5f5U,\r
+ 0xbcbcbcbcU, 0xb6b6b6b6U, 0xdadadadaU, 0x21212121U,\r
+ 0x10101010U, 0xffffffffU, 0xf3f3f3f3U, 0xd2d2d2d2U,\r
+ 0xcdcdcdcdU, 0x0c0c0c0cU, 0x13131313U, 0xececececU,\r
+ 0x5f5f5f5fU, 0x97979797U, 0x44444444U, 0x17171717U,\r
+ 0xc4c4c4c4U, 0xa7a7a7a7U, 0x7e7e7e7eU, 0x3d3d3d3dU,\r
+ 0x64646464U, 0x5d5d5d5dU, 0x19191919U, 0x73737373U,\r
+ 0x60606060U, 0x81818181U, 0x4f4f4f4fU, 0xdcdcdcdcU,\r
+ 0x22222222U, 0x2a2a2a2aU, 0x90909090U, 0x88888888U,\r
+ 0x46464646U, 0xeeeeeeeeU, 0xb8b8b8b8U, 0x14141414U,\r
+ 0xdedededeU, 0x5e5e5e5eU, 0x0b0b0b0bU, 0xdbdbdbdbU,\r
+ 0xe0e0e0e0U, 0x32323232U, 0x3a3a3a3aU, 0x0a0a0a0aU,\r
+ 0x49494949U, 0x06060606U, 0x24242424U, 0x5c5c5c5cU,\r
+ 0xc2c2c2c2U, 0xd3d3d3d3U, 0xacacacacU, 0x62626262U,\r
+ 0x91919191U, 0x95959595U, 0xe4e4e4e4U, 0x79797979U,\r
+ 0xe7e7e7e7U, 0xc8c8c8c8U, 0x37373737U, 0x6d6d6d6dU,\r
+ 0x8d8d8d8dU, 0xd5d5d5d5U, 0x4e4e4e4eU, 0xa9a9a9a9U,\r
+ 0x6c6c6c6cU, 0x56565656U, 0xf4f4f4f4U, 0xeaeaeaeaU,\r
+ 0x65656565U, 0x7a7a7a7aU, 0xaeaeaeaeU, 0x08080808U,\r
+ 0xbabababaU, 0x78787878U, 0x25252525U, 0x2e2e2e2eU,\r
+ 0x1c1c1c1cU, 0xa6a6a6a6U, 0xb4b4b4b4U, 0xc6c6c6c6U,\r
+ 0xe8e8e8e8U, 0xddddddddU, 0x74747474U, 0x1f1f1f1fU,\r
+ 0x4b4b4b4bU, 0xbdbdbdbdU, 0x8b8b8b8bU, 0x8a8a8a8aU,\r
+ 0x70707070U, 0x3e3e3e3eU, 0xb5b5b5b5U, 0x66666666U,\r
+ 0x48484848U, 0x03030303U, 0xf6f6f6f6U, 0x0e0e0e0eU,\r
+ 0x61616161U, 0x35353535U, 0x57575757U, 0xb9b9b9b9U,\r
+ 0x86868686U, 0xc1c1c1c1U, 0x1d1d1d1dU, 0x9e9e9e9eU,\r
+ 0xe1e1e1e1U, 0xf8f8f8f8U, 0x98989898U, 0x11111111U,\r
+ 0x69696969U, 0xd9d9d9d9U, 0x8e8e8e8eU, 0x94949494U,\r
+ 0x9b9b9b9bU, 0x1e1e1e1eU, 0x87878787U, 0xe9e9e9e9U,\r
+ 0xcecececeU, 0x55555555U, 0x28282828U, 0xdfdfdfdfU,\r
+ 0x8c8c8c8cU, 0xa1a1a1a1U, 0x89898989U, 0x0d0d0d0dU,\r
+ 0xbfbfbfbfU, 0xe6e6e6e6U, 0x42424242U, 0x68686868U,\r
+ 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU,\r
+ 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U,\r
+};\r
+static const u32 Td0[256] = {\r
+ 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U,\r
+ 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U,\r
+ 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U,\r
+ 0x4fe5d7fcU, 0xc52acbd7U, 0x26354480U, 0xb562a38fU,\r
+ 0xdeb15a49U, 0x25ba1b67U, 0x45ea0e98U, 0x5dfec0e1U,\r
+ 0xc32f7502U, 0x814cf012U, 0x8d4697a3U, 0x6bd3f9c6U,\r
+ 0x038f5fe7U, 0x15929c95U, 0xbf6d7aebU, 0x955259daU,\r
+ 0xd4be832dU, 0x587421d3U, 0x49e06929U, 0x8ec9c844U,\r
+ 0x75c2896aU, 0xf48e7978U, 0x99583e6bU, 0x27b971ddU,\r
+ 0xbee14fb6U, 0xf088ad17U, 0xc920ac66U, 0x7dce3ab4U,\r
+ 0x63df4a18U, 0xe51a3182U, 0x97513360U, 0x62537f45U,\r
+ 0xb16477e0U, 0xbb6bae84U, 0xfe81a01cU, 0xf9082b94U,\r
+ 0x70486858U, 0x8f45fd19U, 0x94de6c87U, 0x527bf8b7U,\r
+ 0xab73d323U, 0x724b02e2U, 0xe31f8f57U, 0x6655ab2aU,\r
+ 0xb2eb2807U, 0x2fb5c203U, 0x86c57b9aU, 0xd33708a5U,\r
+ 0x302887f2U, 0x23bfa5b2U, 0x02036abaU, 0xed16825cU,\r
+ 0x8acf1c2bU, 0xa779b492U, 0xf307f2f0U, 0x4e69e2a1U,\r
+ 0x65daf4cdU, 0x0605bed5U, 0xd134621fU, 0xc4a6fe8aU,\r
+ 0x342e539dU, 0xa2f355a0U, 0x058ae132U, 0xa4f6eb75U,\r
+ 0x0b83ec39U, 0x4060efaaU, 0x5e719f06U, 0xbd6e1051U,\r
+ 0x3e218af9U, 0x96dd063dU, 0xdd3e05aeU, 0x4de6bd46U,\r
+ 0x91548db5U, 0x71c45d05U, 0x0406d46fU, 0x605015ffU,\r
+ 0x1998fb24U, 0xd6bde997U, 0x894043ccU, 0x67d99e77U,\r
+ 0xb0e842bdU, 0x07898b88U, 0xe7195b38U, 0x79c8eedbU,\r
+ 0xa17c0a47U, 0x7c420fe9U, 0xf8841ec9U, 0x00000000U,\r
+ 0x09808683U, 0x322bed48U, 0x1e1170acU, 0x6c5a724eU,\r
+ 0xfd0efffbU, 0x0f853856U, 0x3daed51eU, 0x362d3927U,\r
+ 0x0a0fd964U, 0x685ca621U, 0x9b5b54d1U, 0x24362e3aU,\r
+ 0x0c0a67b1U, 0x9357e70fU, 0xb4ee96d2U, 0x1b9b919eU,\r
+ 0x80c0c54fU, 0x61dc20a2U, 0x5a774b69U, 0x1c121a16U,\r
+ 0xe293ba0aU, 0xc0a02ae5U, 0x3c22e043U, 0x121b171dU,\r
+ 0x0e090d0bU, 0xf28bc7adU, 0x2db6a8b9U, 0x141ea9c8U,\r
+ 0x57f11985U, 0xaf75074cU, 0xee99ddbbU, 0xa37f60fdU,\r
+ 0xf701269fU, 0x5c72f5bcU, 0x44663bc5U, 0x5bfb7e34U,\r
+ 0x8b432976U, 0xcb23c6dcU, 0xb6edfc68U, 0xb8e4f163U,\r
+ 0xd731dccaU, 0x42638510U, 0x13972240U, 0x84c61120U,\r
+ 0x854a247dU, 0xd2bb3df8U, 0xaef93211U, 0xc729a16dU,\r
+ 0x1d9e2f4bU, 0xdcb230f3U, 0x0d8652ecU, 0x77c1e3d0U,\r
+ 0x2bb3166cU, 0xa970b999U, 0x119448faU, 0x47e96422U,\r
+ 0xa8fc8cc4U, 0xa0f03f1aU, 0x567d2cd8U, 0x223390efU,\r
+ 0x87494ec7U, 0xd938d1c1U, 0x8ccaa2feU, 0x98d40b36U,\r
+ 0xa6f581cfU, 0xa57ade28U, 0xdab78e26U, 0x3fadbfa4U,\r
+ 0x2c3a9de4U, 0x5078920dU, 0x6a5fcc9bU, 0x547e4662U,\r
+ 0xf68d13c2U, 0x90d8b8e8U, 0x2e39f75eU, 0x82c3aff5U,\r
+ 0x9f5d80beU, 0x69d0937cU, 0x6fd52da9U, 0xcf2512b3U,\r
+ 0xc8ac993bU, 0x10187da7U, 0xe89c636eU, 0xdb3bbb7bU,\r
+ 0xcd267809U, 0x6e5918f4U, 0xec9ab701U, 0x834f9aa8U,\r
+ 0xe6956e65U, 0xaaffe67eU, 0x21bccf08U, 0xef15e8e6U,\r
+ 0xbae79bd9U, 0x4a6f36ceU, 0xea9f09d4U, 0x29b07cd6U,\r
+ 0x31a4b2afU, 0x2a3f2331U, 0xc6a59430U, 0x35a266c0U,\r
+ 0x744ebc37U, 0xfc82caa6U, 0xe090d0b0U, 0x33a7d815U,\r
+ 0xf104984aU, 0x41ecdaf7U, 0x7fcd500eU, 0x1791f62fU,\r
+ 0x764dd68dU, 0x43efb04dU, 0xccaa4d54U, 0xe49604dfU,\r
+ 0x9ed1b5e3U, 0x4c6a881bU, 0xc12c1fb8U, 0x4665517fU,\r
+ 0x9d5eea04U, 0x018c355dU, 0xfa877473U, 0xfb0b412eU,\r
+ 0xb3671d5aU, 0x92dbd252U, 0xe9105633U, 0x6dd64713U,\r
+ 0x9ad7618cU, 0x37a10c7aU, 0x59f8148eU, 0xeb133c89U,\r
+ 0xcea927eeU, 0xb761c935U, 0xe11ce5edU, 0x7a47b13cU,\r
+ 0x9cd2df59U, 0x55f2733fU, 0x1814ce79U, 0x73c737bfU,\r
+ 0x53f7cdeaU, 0x5ffdaa5bU, 0xdf3d6f14U, 0x7844db86U,\r
+ 0xcaaff381U, 0xb968c43eU, 0x3824342cU, 0xc2a3405fU,\r
+ 0x161dc372U, 0xbce2250cU, 0x283c498bU, 0xff0d9541U,\r
+ 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U,\r
+ 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U,\r
+};\r
+static const u32 Td1[256] = {\r
+ 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU,\r
+ 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U,\r
+ 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU,\r
+ 0xfc4fe5d7U, 0xd7c52acbU, 0x80263544U, 0x8fb562a3U,\r
+ 0x49deb15aU, 0x6725ba1bU, 0x9845ea0eU, 0xe15dfec0U,\r
+ 0x02c32f75U, 0x12814cf0U, 0xa38d4697U, 0xc66bd3f9U,\r
+ 0xe7038f5fU, 0x9515929cU, 0xebbf6d7aU, 0xda955259U,\r
+ 0x2dd4be83U, 0xd3587421U, 0x2949e069U, 0x448ec9c8U,\r
+ 0x6a75c289U, 0x78f48e79U, 0x6b99583eU, 0xdd27b971U,\r
+ 0xb6bee14fU, 0x17f088adU, 0x66c920acU, 0xb47dce3aU,\r
+ 0x1863df4aU, 0x82e51a31U, 0x60975133U, 0x4562537fU,\r
+ 0xe0b16477U, 0x84bb6baeU, 0x1cfe81a0U, 0x94f9082bU,\r
+ 0x58704868U, 0x198f45fdU, 0x8794de6cU, 0xb7527bf8U,\r
+ 0x23ab73d3U, 0xe2724b02U, 0x57e31f8fU, 0x2a6655abU,\r
+ 0x07b2eb28U, 0x032fb5c2U, 0x9a86c57bU, 0xa5d33708U,\r
+ 0xf2302887U, 0xb223bfa5U, 0xba02036aU, 0x5ced1682U,\r
+ 0x2b8acf1cU, 0x92a779b4U, 0xf0f307f2U, 0xa14e69e2U,\r
+ 0xcd65daf4U, 0xd50605beU, 0x1fd13462U, 0x8ac4a6feU,\r
+ 0x9d342e53U, 0xa0a2f355U, 0x32058ae1U, 0x75a4f6ebU,\r
+ 0x390b83ecU, 0xaa4060efU, 0x065e719fU, 0x51bd6e10U,\r
+ 0xf93e218aU, 0x3d96dd06U, 0xaedd3e05U, 0x464de6bdU,\r
+ 0xb591548dU, 0x0571c45dU, 0x6f0406d4U, 0xff605015U,\r
+ 0x241998fbU, 0x97d6bde9U, 0xcc894043U, 0x7767d99eU,\r
+ 0xbdb0e842U, 0x8807898bU, 0x38e7195bU, 0xdb79c8eeU,\r
+ 0x47a17c0aU, 0xe97c420fU, 0xc9f8841eU, 0x00000000U,\r
+ 0x83098086U, 0x48322bedU, 0xac1e1170U, 0x4e6c5a72U,\r
+ 0xfbfd0effU, 0x560f8538U, 0x1e3daed5U, 0x27362d39U,\r
+ 0x640a0fd9U, 0x21685ca6U, 0xd19b5b54U, 0x3a24362eU,\r
+ 0xb10c0a67U, 0x0f9357e7U, 0xd2b4ee96U, 0x9e1b9b91U,\r
+ 0x4f80c0c5U, 0xa261dc20U, 0x695a774bU, 0x161c121aU,\r
+ 0x0ae293baU, 0xe5c0a02aU, 0x433c22e0U, 0x1d121b17U,\r
+ 0x0b0e090dU, 0xadf28bc7U, 0xb92db6a8U, 0xc8141ea9U,\r
+ 0x8557f119U, 0x4caf7507U, 0xbbee99ddU, 0xfda37f60U,\r
+ 0x9ff70126U, 0xbc5c72f5U, 0xc544663bU, 0x345bfb7eU,\r
+ 0x768b4329U, 0xdccb23c6U, 0x68b6edfcU, 0x63b8e4f1U,\r
+ 0xcad731dcU, 0x10426385U, 0x40139722U, 0x2084c611U,\r
+ 0x7d854a24U, 0xf8d2bb3dU, 0x11aef932U, 0x6dc729a1U,\r
+ 0x4b1d9e2fU, 0xf3dcb230U, 0xec0d8652U, 0xd077c1e3U,\r
+ 0x6c2bb316U, 0x99a970b9U, 0xfa119448U, 0x2247e964U,\r
+ 0xc4a8fc8cU, 0x1aa0f03fU, 0xd8567d2cU, 0xef223390U,\r
+ 0xc787494eU, 0xc1d938d1U, 0xfe8ccaa2U, 0x3698d40bU,\r
+ 0xcfa6f581U, 0x28a57adeU, 0x26dab78eU, 0xa43fadbfU,\r
+ 0xe42c3a9dU, 0x0d507892U, 0x9b6a5fccU, 0x62547e46U,\r
+ 0xc2f68d13U, 0xe890d8b8U, 0x5e2e39f7U, 0xf582c3afU,\r
+ 0xbe9f5d80U, 0x7c69d093U, 0xa96fd52dU, 0xb3cf2512U,\r
+ 0x3bc8ac99U, 0xa710187dU, 0x6ee89c63U, 0x7bdb3bbbU,\r
+ 0x09cd2678U, 0xf46e5918U, 0x01ec9ab7U, 0xa8834f9aU,\r
+ 0x65e6956eU, 0x7eaaffe6U, 0x0821bccfU, 0xe6ef15e8U,\r
+ 0xd9bae79bU, 0xce4a6f36U, 0xd4ea9f09U, 0xd629b07cU,\r
+ 0xaf31a4b2U, 0x312a3f23U, 0x30c6a594U, 0xc035a266U,\r
+ 0x37744ebcU, 0xa6fc82caU, 0xb0e090d0U, 0x1533a7d8U,\r
+ 0x4af10498U, 0xf741ecdaU, 0x0e7fcd50U, 0x2f1791f6U,\r
+ 0x8d764dd6U, 0x4d43efb0U, 0x54ccaa4dU, 0xdfe49604U,\r
+ 0xe39ed1b5U, 0x1b4c6a88U, 0xb8c12c1fU, 0x7f466551U,\r
+ 0x049d5eeaU, 0x5d018c35U, 0x73fa8774U, 0x2efb0b41U,\r
+ 0x5ab3671dU, 0x5292dbd2U, 0x33e91056U, 0x136dd647U,\r
+ 0x8c9ad761U, 0x7a37a10cU, 0x8e59f814U, 0x89eb133cU,\r
+ 0xeecea927U, 0x35b761c9U, 0xede11ce5U, 0x3c7a47b1U,\r
+ 0x599cd2dfU, 0x3f55f273U, 0x791814ceU, 0xbf73c737U,\r
+ 0xea53f7cdU, 0x5b5ffdaaU, 0x14df3d6fU, 0x867844dbU,\r
+ 0x81caaff3U, 0x3eb968c4U, 0x2c382434U, 0x5fc2a340U,\r
+ 0x72161dc3U, 0x0cbce225U, 0x8b283c49U, 0x41ff0d95U,\r
+ 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U,\r
+ 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U,\r
+};\r
+static const u32 Td2[256] = {\r
+ 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U,\r
+ 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U,\r
+ 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U,\r
+ 0xd7fc4fe5U, 0xcbd7c52aU, 0x44802635U, 0xa38fb562U,\r
+ 0x5a49deb1U, 0x1b6725baU, 0x0e9845eaU, 0xc0e15dfeU,\r
+ 0x7502c32fU, 0xf012814cU, 0x97a38d46U, 0xf9c66bd3U,\r
+ 0x5fe7038fU, 0x9c951592U, 0x7aebbf6dU, 0x59da9552U,\r
+ 0x832dd4beU, 0x21d35874U, 0x692949e0U, 0xc8448ec9U,\r
+ 0x896a75c2U, 0x7978f48eU, 0x3e6b9958U, 0x71dd27b9U,\r
+ 0x4fb6bee1U, 0xad17f088U, 0xac66c920U, 0x3ab47dceU,\r
+ 0x4a1863dfU, 0x3182e51aU, 0x33609751U, 0x7f456253U,\r
+ 0x77e0b164U, 0xae84bb6bU, 0xa01cfe81U, 0x2b94f908U,\r
+ 0x68587048U, 0xfd198f45U, 0x6c8794deU, 0xf8b7527bU,\r
+ 0xd323ab73U, 0x02e2724bU, 0x8f57e31fU, 0xab2a6655U,\r
+ 0x2807b2ebU, 0xc2032fb5U, 0x7b9a86c5U, 0x08a5d337U,\r
+ 0x87f23028U, 0xa5b223bfU, 0x6aba0203U, 0x825ced16U,\r
+ 0x1c2b8acfU, 0xb492a779U, 0xf2f0f307U, 0xe2a14e69U,\r
+ 0xf4cd65daU, 0xbed50605U, 0x621fd134U, 0xfe8ac4a6U,\r
+ 0x539d342eU, 0x55a0a2f3U, 0xe132058aU, 0xeb75a4f6U,\r
+ 0xec390b83U, 0xefaa4060U, 0x9f065e71U, 0x1051bd6eU,\r
+\r
+ 0x8af93e21U, 0x063d96ddU, 0x05aedd3eU, 0xbd464de6U,\r
+ 0x8db59154U, 0x5d0571c4U, 0xd46f0406U, 0x15ff6050U,\r
+ 0xfb241998U, 0xe997d6bdU, 0x43cc8940U, 0x9e7767d9U,\r
+ 0x42bdb0e8U, 0x8b880789U, 0x5b38e719U, 0xeedb79c8U,\r
+ 0x0a47a17cU, 0x0fe97c42U, 0x1ec9f884U, 0x00000000U,\r
+ 0x86830980U, 0xed48322bU, 0x70ac1e11U, 0x724e6c5aU,\r
+ 0xfffbfd0eU, 0x38560f85U, 0xd51e3daeU, 0x3927362dU,\r
+ 0xd9640a0fU, 0xa621685cU, 0x54d19b5bU, 0x2e3a2436U,\r
+ 0x67b10c0aU, 0xe70f9357U, 0x96d2b4eeU, 0x919e1b9bU,\r
+ 0xc54f80c0U, 0x20a261dcU, 0x4b695a77U, 0x1a161c12U,\r
+ 0xba0ae293U, 0x2ae5c0a0U, 0xe0433c22U, 0x171d121bU,\r
+ 0x0d0b0e09U, 0xc7adf28bU, 0xa8b92db6U, 0xa9c8141eU,\r
+ 0x198557f1U, 0x074caf75U, 0xddbbee99U, 0x60fda37fU,\r
+ 0x269ff701U, 0xf5bc5c72U, 0x3bc54466U, 0x7e345bfbU,\r
+ 0x29768b43U, 0xc6dccb23U, 0xfc68b6edU, 0xf163b8e4U,\r
+ 0xdccad731U, 0x85104263U, 0x22401397U, 0x112084c6U,\r
+ 0x247d854aU, 0x3df8d2bbU, 0x3211aef9U, 0xa16dc729U,\r
+ 0x2f4b1d9eU, 0x30f3dcb2U, 0x52ec0d86U, 0xe3d077c1U,\r
+ 0x166c2bb3U, 0xb999a970U, 0x48fa1194U, 0x642247e9U,\r
+ 0x8cc4a8fcU, 0x3f1aa0f0U, 0x2cd8567dU, 0x90ef2233U,\r
+ 0x4ec78749U, 0xd1c1d938U, 0xa2fe8ccaU, 0x0b3698d4U,\r
+ 0x81cfa6f5U, 0xde28a57aU, 0x8e26dab7U, 0xbfa43fadU,\r
+ 0x9de42c3aU, 0x920d5078U, 0xcc9b6a5fU, 0x4662547eU,\r
+ 0x13c2f68dU, 0xb8e890d8U, 0xf75e2e39U, 0xaff582c3U,\r
+ 0x80be9f5dU, 0x937c69d0U, 0x2da96fd5U, 0x12b3cf25U,\r
+ 0x993bc8acU, 0x7da71018U, 0x636ee89cU, 0xbb7bdb3bU,\r
+ 0x7809cd26U, 0x18f46e59U, 0xb701ec9aU, 0x9aa8834fU,\r
+ 0x6e65e695U, 0xe67eaaffU, 0xcf0821bcU, 0xe8e6ef15U,\r
+ 0x9bd9bae7U, 0x36ce4a6fU, 0x09d4ea9fU, 0x7cd629b0U,\r
+ 0xb2af31a4U, 0x23312a3fU, 0x9430c6a5U, 0x66c035a2U,\r
+ 0xbc37744eU, 0xcaa6fc82U, 0xd0b0e090U, 0xd81533a7U,\r
+ 0x984af104U, 0xdaf741ecU, 0x500e7fcdU, 0xf62f1791U,\r
+ 0xd68d764dU, 0xb04d43efU, 0x4d54ccaaU, 0x04dfe496U,\r
+ 0xb5e39ed1U, 0x881b4c6aU, 0x1fb8c12cU, 0x517f4665U,\r
+ 0xea049d5eU, 0x355d018cU, 0x7473fa87U, 0x412efb0bU,\r
+ 0x1d5ab367U, 0xd25292dbU, 0x5633e910U, 0x47136dd6U,\r
+ 0x618c9ad7U, 0x0c7a37a1U, 0x148e59f8U, 0x3c89eb13U,\r
+ 0x27eecea9U, 0xc935b761U, 0xe5ede11cU, 0xb13c7a47U,\r
+ 0xdf599cd2U, 0x733f55f2U, 0xce791814U, 0x37bf73c7U,\r
+ 0xcdea53f7U, 0xaa5b5ffdU, 0x6f14df3dU, 0xdb867844U,\r
+ 0xf381caafU, 0xc43eb968U, 0x342c3824U, 0x405fc2a3U,\r
+ 0xc372161dU, 0x250cbce2U, 0x498b283cU, 0x9541ff0dU,\r
+ 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U,\r
+ 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U,\r
+};\r
+static const u32 Td3[256] = {\r
+ 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU,\r
+ 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU,\r
+ 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U,\r
+ 0xe5d7fc4fU, 0x2acbd7c5U, 0x35448026U, 0x62a38fb5U,\r
+ 0xb15a49deU, 0xba1b6725U, 0xea0e9845U, 0xfec0e15dU,\r
+ 0x2f7502c3U, 0x4cf01281U, 0x4697a38dU, 0xd3f9c66bU,\r
+ 0x8f5fe703U, 0x929c9515U, 0x6d7aebbfU, 0x5259da95U,\r
+ 0xbe832dd4U, 0x7421d358U, 0xe0692949U, 0xc9c8448eU,\r
+ 0xc2896a75U, 0x8e7978f4U, 0x583e6b99U, 0xb971dd27U,\r
+ 0xe14fb6beU, 0x88ad17f0U, 0x20ac66c9U, 0xce3ab47dU,\r
+ 0xdf4a1863U, 0x1a3182e5U, 0x51336097U, 0x537f4562U,\r
+ 0x6477e0b1U, 0x6bae84bbU, 0x81a01cfeU, 0x082b94f9U,\r
+ 0x48685870U, 0x45fd198fU, 0xde6c8794U, 0x7bf8b752U,\r
+ 0x73d323abU, 0x4b02e272U, 0x1f8f57e3U, 0x55ab2a66U,\r
+ 0xeb2807b2U, 0xb5c2032fU, 0xc57b9a86U, 0x3708a5d3U,\r
+ 0x2887f230U, 0xbfa5b223U, 0x036aba02U, 0x16825cedU,\r
+ 0xcf1c2b8aU, 0x79b492a7U, 0x07f2f0f3U, 0x69e2a14eU,\r
+ 0xdaf4cd65U, 0x05bed506U, 0x34621fd1U, 0xa6fe8ac4U,\r
+ 0x2e539d34U, 0xf355a0a2U, 0x8ae13205U, 0xf6eb75a4U,\r
+ 0x83ec390bU, 0x60efaa40U, 0x719f065eU, 0x6e1051bdU,\r
+ 0x218af93eU, 0xdd063d96U, 0x3e05aeddU, 0xe6bd464dU,\r
+ 0x548db591U, 0xc45d0571U, 0x06d46f04U, 0x5015ff60U,\r
+ 0x98fb2419U, 0xbde997d6U, 0x4043cc89U, 0xd99e7767U,\r
+ 0xe842bdb0U, 0x898b8807U, 0x195b38e7U, 0xc8eedb79U,\r
+ 0x7c0a47a1U, 0x420fe97cU, 0x841ec9f8U, 0x00000000U,\r
+ 0x80868309U, 0x2bed4832U, 0x1170ac1eU, 0x5a724e6cU,\r
+ 0x0efffbfdU, 0x8538560fU, 0xaed51e3dU, 0x2d392736U,\r
+ 0x0fd9640aU, 0x5ca62168U, 0x5b54d19bU, 0x362e3a24U,\r
+ 0x0a67b10cU, 0x57e70f93U, 0xee96d2b4U, 0x9b919e1bU,\r
+ 0xc0c54f80U, 0xdc20a261U, 0x774b695aU, 0x121a161cU,\r
+ 0x93ba0ae2U, 0xa02ae5c0U, 0x22e0433cU, 0x1b171d12U,\r
+ 0x090d0b0eU, 0x8bc7adf2U, 0xb6a8b92dU, 0x1ea9c814U,\r
+ 0xf1198557U, 0x75074cafU, 0x99ddbbeeU, 0x7f60fda3U,\r
+ 0x01269ff7U, 0x72f5bc5cU, 0x663bc544U, 0xfb7e345bU,\r
+ 0x4329768bU, 0x23c6dccbU, 0xedfc68b6U, 0xe4f163b8U,\r
+ 0x31dccad7U, 0x63851042U, 0x97224013U, 0xc6112084U,\r
+ 0x4a247d85U, 0xbb3df8d2U, 0xf93211aeU, 0x29a16dc7U,\r
+ 0x9e2f4b1dU, 0xb230f3dcU, 0x8652ec0dU, 0xc1e3d077U,\r
+ 0xb3166c2bU, 0x70b999a9U, 0x9448fa11U, 0xe9642247U,\r
+ 0xfc8cc4a8U, 0xf03f1aa0U, 0x7d2cd856U, 0x3390ef22U,\r
+ 0x494ec787U, 0x38d1c1d9U, 0xcaa2fe8cU, 0xd40b3698U,\r
+ 0xf581cfa6U, 0x7ade28a5U, 0xb78e26daU, 0xadbfa43fU,\r
+ 0x3a9de42cU, 0x78920d50U, 0x5fcc9b6aU, 0x7e466254U,\r
+ 0x8d13c2f6U, 0xd8b8e890U, 0x39f75e2eU, 0xc3aff582U,\r
+ 0x5d80be9fU, 0xd0937c69U, 0xd52da96fU, 0x2512b3cfU,\r
+ 0xac993bc8U, 0x187da710U, 0x9c636ee8U, 0x3bbb7bdbU,\r
+ 0x267809cdU, 0x5918f46eU, 0x9ab701ecU, 0x4f9aa883U,\r
+ 0x956e65e6U, 0xffe67eaaU, 0xbccf0821U, 0x15e8e6efU,\r
+ 0xe79bd9baU, 0x6f36ce4aU, 0x9f09d4eaU, 0xb07cd629U,\r
+ 0xa4b2af31U, 0x3f23312aU, 0xa59430c6U, 0xa266c035U,\r
+ 0x4ebc3774U, 0x82caa6fcU, 0x90d0b0e0U, 0xa7d81533U,\r
+ 0x04984af1U, 0xecdaf741U, 0xcd500e7fU, 0x91f62f17U,\r
+ 0x4dd68d76U, 0xefb04d43U, 0xaa4d54ccU, 0x9604dfe4U,\r
+ 0xd1b5e39eU, 0x6a881b4cU, 0x2c1fb8c1U, 0x65517f46U,\r
+ 0x5eea049dU, 0x8c355d01U, 0x877473faU, 0x0b412efbU,\r
+ 0x671d5ab3U, 0xdbd25292U, 0x105633e9U, 0xd647136dU,\r
+ 0xd7618c9aU, 0xa10c7a37U, 0xf8148e59U, 0x133c89ebU,\r
+ 0xa927eeceU, 0x61c935b7U, 0x1ce5ede1U, 0x47b13c7aU,\r
+ 0xd2df599cU, 0xf2733f55U, 0x14ce7918U, 0xc737bf73U,\r
+ 0xf7cdea53U, 0xfdaa5b5fU, 0x3d6f14dfU, 0x44db8678U,\r
+ 0xaff381caU, 0x68c43eb9U, 0x24342c38U, 0xa3405fc2U,\r
+ 0x1dc37216U, 0xe2250cbcU, 0x3c498b28U, 0x0d9541ffU,\r
+ 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U,\r
+ 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U,\r
+};\r
+static const u32 Td4[256] = {\r
+ 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U,\r
+ 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U,\r
+ 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU,\r
+ 0x81818181U, 0xf3f3f3f3U, 0xd7d7d7d7U, 0xfbfbfbfbU,\r
+ 0x7c7c7c7cU, 0xe3e3e3e3U, 0x39393939U, 0x82828282U,\r
+ 0x9b9b9b9bU, 0x2f2f2f2fU, 0xffffffffU, 0x87878787U,\r
+ 0x34343434U, 0x8e8e8e8eU, 0x43434343U, 0x44444444U,\r
+ 0xc4c4c4c4U, 0xdedededeU, 0xe9e9e9e9U, 0xcbcbcbcbU,\r
+ 0x54545454U, 0x7b7b7b7bU, 0x94949494U, 0x32323232U,\r
+ 0xa6a6a6a6U, 0xc2c2c2c2U, 0x23232323U, 0x3d3d3d3dU,\r
+ 0xeeeeeeeeU, 0x4c4c4c4cU, 0x95959595U, 0x0b0b0b0bU,\r
+ 0x42424242U, 0xfafafafaU, 0xc3c3c3c3U, 0x4e4e4e4eU,\r
+ 0x08080808U, 0x2e2e2e2eU, 0xa1a1a1a1U, 0x66666666U,\r
+ 0x28282828U, 0xd9d9d9d9U, 0x24242424U, 0xb2b2b2b2U,\r
+ 0x76767676U, 0x5b5b5b5bU, 0xa2a2a2a2U, 0x49494949U,\r
+ 0x6d6d6d6dU, 0x8b8b8b8bU, 0xd1d1d1d1U, 0x25252525U,\r
+ 0x72727272U, 0xf8f8f8f8U, 0xf6f6f6f6U, 0x64646464U,\r
+ 0x86868686U, 0x68686868U, 0x98989898U, 0x16161616U,\r
+ 0xd4d4d4d4U, 0xa4a4a4a4U, 0x5c5c5c5cU, 0xccccccccU,\r
+ 0x5d5d5d5dU, 0x65656565U, 0xb6b6b6b6U, 0x92929292U,\r
+ 0x6c6c6c6cU, 0x70707070U, 0x48484848U, 0x50505050U,\r
+ 0xfdfdfdfdU, 0xededededU, 0xb9b9b9b9U, 0xdadadadaU,\r
+ 0x5e5e5e5eU, 0x15151515U, 0x46464646U, 0x57575757U,\r
+ 0xa7a7a7a7U, 0x8d8d8d8dU, 0x9d9d9d9dU, 0x84848484U,\r
+ 0x90909090U, 0xd8d8d8d8U, 0xababababU, 0x00000000U,\r
+ 0x8c8c8c8cU, 0xbcbcbcbcU, 0xd3d3d3d3U, 0x0a0a0a0aU,\r
+ 0xf7f7f7f7U, 0xe4e4e4e4U, 0x58585858U, 0x05050505U,\r
+ 0xb8b8b8b8U, 0xb3b3b3b3U, 0x45454545U, 0x06060606U,\r
+ 0xd0d0d0d0U, 0x2c2c2c2cU, 0x1e1e1e1eU, 0x8f8f8f8fU,\r
+ 0xcacacacaU, 0x3f3f3f3fU, 0x0f0f0f0fU, 0x02020202U,\r
+ 0xc1c1c1c1U, 0xafafafafU, 0xbdbdbdbdU, 0x03030303U,\r
+ 0x01010101U, 0x13131313U, 0x8a8a8a8aU, 0x6b6b6b6bU,\r
+ 0x3a3a3a3aU, 0x91919191U, 0x11111111U, 0x41414141U,\r
+ 0x4f4f4f4fU, 0x67676767U, 0xdcdcdcdcU, 0xeaeaeaeaU,\r
+ 0x97979797U, 0xf2f2f2f2U, 0xcfcfcfcfU, 0xcecececeU,\r
+ 0xf0f0f0f0U, 0xb4b4b4b4U, 0xe6e6e6e6U, 0x73737373U,\r
+ 0x96969696U, 0xacacacacU, 0x74747474U, 0x22222222U,\r
+ 0xe7e7e7e7U, 0xadadadadU, 0x35353535U, 0x85858585U,\r
+ 0xe2e2e2e2U, 0xf9f9f9f9U, 0x37373737U, 0xe8e8e8e8U,\r
+ 0x1c1c1c1cU, 0x75757575U, 0xdfdfdfdfU, 0x6e6e6e6eU,\r
+ 0x47474747U, 0xf1f1f1f1U, 0x1a1a1a1aU, 0x71717171U,\r
+ 0x1d1d1d1dU, 0x29292929U, 0xc5c5c5c5U, 0x89898989U,\r
+ 0x6f6f6f6fU, 0xb7b7b7b7U, 0x62626262U, 0x0e0e0e0eU,\r
+ 0xaaaaaaaaU, 0x18181818U, 0xbebebebeU, 0x1b1b1b1bU,\r
+ 0xfcfcfcfcU, 0x56565656U, 0x3e3e3e3eU, 0x4b4b4b4bU,\r
+ 0xc6c6c6c6U, 0xd2d2d2d2U, 0x79797979U, 0x20202020U,\r
+ 0x9a9a9a9aU, 0xdbdbdbdbU, 0xc0c0c0c0U, 0xfefefefeU,\r
+ 0x78787878U, 0xcdcdcdcdU, 0x5a5a5a5aU, 0xf4f4f4f4U,\r
+ 0x1f1f1f1fU, 0xddddddddU, 0xa8a8a8a8U, 0x33333333U,\r
+ 0x88888888U, 0x07070707U, 0xc7c7c7c7U, 0x31313131U,\r
+ 0xb1b1b1b1U, 0x12121212U, 0x10101010U, 0x59595959U,\r
+ 0x27272727U, 0x80808080U, 0xececececU, 0x5f5f5f5fU,\r
+ 0x60606060U, 0x51515151U, 0x7f7f7f7fU, 0xa9a9a9a9U,\r
+ 0x19191919U, 0xb5b5b5b5U, 0x4a4a4a4aU, 0x0d0d0d0dU,\r
+ 0x2d2d2d2dU, 0xe5e5e5e5U, 0x7a7a7a7aU, 0x9f9f9f9fU,\r
+ 0x93939393U, 0xc9c9c9c9U, 0x9c9c9c9cU, 0xefefefefU,\r
+ 0xa0a0a0a0U, 0xe0e0e0e0U, 0x3b3b3b3bU, 0x4d4d4d4dU,\r
+ 0xaeaeaeaeU, 0x2a2a2a2aU, 0xf5f5f5f5U, 0xb0b0b0b0U,\r
+ 0xc8c8c8c8U, 0xebebebebU, 0xbbbbbbbbU, 0x3c3c3c3cU,\r
+ 0x83838383U, 0x53535353U, 0x99999999U, 0x61616161U,\r
+ 0x17171717U, 0x2b2b2b2bU, 0x04040404U, 0x7e7e7e7eU,\r
+ 0xbabababaU, 0x77777777U, 0xd6d6d6d6U, 0x26262626U,\r
+ 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U,\r
+ 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU,\r
+};\r
+static const u32 rcon[] = {\r
+ 0x01000000, 0x02000000, 0x04000000, 0x08000000,\r
+ 0x10000000, 0x20000000, 0x40000000, 0x80000000,\r
+ 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */\r
+};\r
+\r
+/**\r
+ * Expand the cipher key into the encryption key schedule.\r
+ */\r
+int AES_set_encrypt_key(const unsigned char *userKey, const int bits,\r
+ AES_KEY *key) {\r
+\r
+ u32 *rk;\r
+ int i = 0;\r
+ u32 temp;\r
+\r
+ if (!userKey || !key)\r
+ return -1;\r
+ if (bits != 128 && bits != 192 && bits != 256)\r
+ return -2;\r
+\r
+ rk = key->rd_key;\r
+\r
+ if (bits==128)\r
+ key->rounds = 10;\r
+ else if (bits==192)\r
+ key->rounds = 12;\r
+ else\r
+ key->rounds = 14;\r
+\r
+ rk[0] = GETU32(userKey );\r
+ rk[1] = GETU32(userKey + 4);\r
+ rk[2] = GETU32(userKey + 8);\r
+ rk[3] = GETU32(userKey + 12);\r
+ if (bits == 128) {\r
+ while (1) {\r
+ temp = rk[3];\r
+ rk[4] = rk[0] ^\r
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^\r
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^\r
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^\r
+ (Te4[(temp >> 24) ] & 0x000000ff) ^\r
+ rcon[i];\r
+ rk[5] = rk[1] ^ rk[4];\r
+ rk[6] = rk[2] ^ rk[5];\r
+ rk[7] = rk[3] ^ rk[6];\r
+ if (++i == 10) {\r
+ return 0;\r
+ }\r
+ rk += 4;\r
+ }\r
+ }\r
+ rk[4] = GETU32(userKey + 16);\r
+ rk[5] = GETU32(userKey + 20);\r
+ if (bits == 192) {\r
+ while (1) {\r
+ temp = rk[ 5];\r
+ rk[ 6] = rk[ 0] ^\r
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^\r
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^\r
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^\r
+ (Te4[(temp >> 24) ] & 0x000000ff) ^\r
+ rcon[i];\r
+ rk[ 7] = rk[ 1] ^ rk[ 6];\r
+ rk[ 8] = rk[ 2] ^ rk[ 7];\r
+ rk[ 9] = rk[ 3] ^ rk[ 8];\r
+ if (++i == 8) {\r
+ return 0;\r
+ }\r
+ rk[10] = rk[ 4] ^ rk[ 9];\r
+ rk[11] = rk[ 5] ^ rk[10];\r
+ rk += 6;\r
+ }\r
+ }\r
+ rk[6] = GETU32(userKey + 24);\r
+ rk[7] = GETU32(userKey + 28);\r
+ if (bits == 256) {\r
+ while (1) {\r
+ temp = rk[ 7];\r
+ rk[ 8] = rk[ 0] ^\r
+ (Te4[(temp >> 16) & 0xff] & 0xff000000) ^\r
+ (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^\r
+ (Te4[(temp ) & 0xff] & 0x0000ff00) ^\r
+ (Te4[(temp >> 24) ] & 0x000000ff) ^\r
+ rcon[i];\r
+ rk[ 9] = rk[ 1] ^ rk[ 8];\r
+ rk[10] = rk[ 2] ^ rk[ 9];\r
+ rk[11] = rk[ 3] ^ rk[10];\r
+ if (++i == 7) {\r
+ return 0;\r
+ }\r
+ temp = rk[11];\r
+ rk[12] = rk[ 4] ^\r
+ (Te4[(temp >> 24) ] & 0xff000000) ^\r
+ (Te4[(temp >> 16) & 0xff] & 0x00ff0000) ^\r
+ (Te4[(temp >> 8) & 0xff] & 0x0000ff00) ^\r
+ (Te4[(temp ) & 0xff] & 0x000000ff);\r
+ rk[13] = rk[ 5] ^ rk[12];\r
+ rk[14] = rk[ 6] ^ rk[13];\r
+ rk[15] = rk[ 7] ^ rk[14];\r
+\r
+ rk += 8;\r
+ }\r
+ }\r
+ return 0;\r
+}\r
+\r
+/**\r
+ * Expand the cipher key into the decryption key schedule.\r
+ */\r
+int AES_set_decrypt_key(const unsigned char *userKey, const int bits,\r
+ AES_KEY *key) {\r
+\r
+ u32 *rk;\r
+ int i, j, status;\r
+ u32 temp;\r
+\r
+ /* first, start with an encryption schedule */\r
+ status = AES_set_encrypt_key(userKey, bits, key);\r
+ if (status < 0)\r
+ return status;\r
+\r
+ rk = key->rd_key;\r
+\r
+ /* invert the order of the round keys: */\r
+ for (i = 0, j = 4*(key->rounds); i < j; i += 4, j -= 4) {\r
+ temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp;\r
+ temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp;\r
+ temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp;\r
+ temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp;\r
+ }\r
+ /* apply the inverse MixColumn transform to all round keys but the first and the last: */\r
+ for (i = 1; i < (key->rounds); i++) {\r
+ rk += 4;\r
+ rk[0] =\r
+ Td0[Te4[(rk[0] >> 24) ] & 0xff] ^\r
+ Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^\r
+ Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^\r
+ Td3[Te4[(rk[0] ) & 0xff] & 0xff];\r
+ rk[1] =\r
+ Td0[Te4[(rk[1] >> 24) ] & 0xff] ^\r
+ Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^\r
+ Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^\r
+ Td3[Te4[(rk[1] ) & 0xff] & 0xff];\r
+ rk[2] =\r
+ Td0[Te4[(rk[2] >> 24) ] & 0xff] ^\r
+ Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^\r
+ Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^\r
+ Td3[Te4[(rk[2] ) & 0xff] & 0xff];\r
+ rk[3] =\r
+ Td0[Te4[(rk[3] >> 24) ] & 0xff] ^\r
+ Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^\r
+ Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^\r
+ Td3[Te4[(rk[3] ) & 0xff] & 0xff];\r
+ }\r
+ return 0;\r
+}\r
+\r
+/*\r
+ * Encrypt a single block\r
+ * in and out can overlap\r
+ */\r
+void AES_encrypt(const unsigned char *in, unsigned char *out,\r
+ const AES_KEY *key) {\r
+\r
+ const u32 *rk;\r
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;\r
+#ifndef FULL_UNROLL\r
+ int r;\r
+#endif /* ?FULL_UNROLL */\r
+\r
+ assert(in && out && key);\r
+ rk = key->rd_key;\r
+\r
+ /*\r
+ * map byte array block to cipher state\r
+ * and add initial round key:\r
+ */\r
+ s0 = GETU32(in ) ^ rk[0];\r
+ s1 = GETU32(in + 4) ^ rk[1];\r
+ s2 = GETU32(in + 8) ^ rk[2];\r
+ s3 = GETU32(in + 12) ^ rk[3];\r
+#ifdef FULL_UNROLL\r
+ /* round 1: */\r
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4];\r
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5];\r
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6];\r
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7];\r
+ /* round 2: */\r
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8];\r
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9];\r
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10];\r
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11];\r
+ /* round 3: */\r
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12];\r
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13];\r
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14];\r
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15];\r
+ /* round 4: */\r
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16];\r
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17];\r
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18];\r
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19];\r
+ /* round 5: */\r
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20];\r
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21];\r
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22];\r
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23];\r
+ /* round 6: */\r
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24];\r
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25];\r
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26];\r
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27];\r
+ /* round 7: */\r
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28];\r
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29];\r
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30];\r
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31];\r
+ /* round 8: */\r
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32];\r
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33];\r
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34];\r
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35];\r
+ /* round 9: */\r
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36];\r
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37];\r
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38];\r
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39];\r
+ if (key->rounds > 10) {\r
+ /* round 10: */\r
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[40];\r
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[41];\r
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[42];\r
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[43];\r
+ /* round 11: */\r
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[44];\r
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[45];\r
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[46];\r
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[47];\r
+ if (key->rounds > 12) {\r
+ /* round 12: */\r
+ s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[48];\r
+ s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[49];\r
+ s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[50];\r
+ s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[51];\r
+ /* round 13: */\r
+ t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[52];\r
+ t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[53];\r
+ t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[54];\r
+ t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[55];\r
+ }\r
+ }\r
+ rk += key->rounds << 2;\r
+#else /* !FULL_UNROLL */\r
+ /*\r
+ * Nr - 1 full rounds:\r
+ */\r
+ r = key->rounds >> 1;\r
+ for (;;) {\r
+ t0 =\r
+ Te0[(s0 >> 24) ] ^\r
+ Te1[(s1 >> 16) & 0xff] ^\r
+ Te2[(s2 >> 8) & 0xff] ^\r
+ Te3[(s3 ) & 0xff] ^\r
+ rk[4];\r
+ t1 =\r
+ Te0[(s1 >> 24) ] ^\r
+ Te1[(s2 >> 16) & 0xff] ^\r
+ Te2[(s3 >> 8) & 0xff] ^\r
+ Te3[(s0 ) & 0xff] ^\r
+ rk[5];\r
+ t2 =\r
+ Te0[(s2 >> 24) ] ^\r
+ Te1[(s3 >> 16) & 0xff] ^\r
+ Te2[(s0 >> 8) & 0xff] ^\r
+ Te3[(s1 ) & 0xff] ^\r
+ rk[6];\r
+ t3 =\r
+ Te0[(s3 >> 24) ] ^\r
+ Te1[(s0 >> 16) & 0xff] ^\r
+ Te2[(s1 >> 8) & 0xff] ^\r
+ Te3[(s2 ) & 0xff] ^\r
+ rk[7];\r
+\r
+ rk += 8;\r
+ if (--r == 0) {\r
+ break;\r
+ }\r
+\r
+ s0 =\r
+ Te0[(t0 >> 24) ] ^\r
+ Te1[(t1 >> 16) & 0xff] ^\r
+ Te2[(t2 >> 8) & 0xff] ^\r
+ Te3[(t3 ) & 0xff] ^\r
+ rk[0];\r
+ s1 =\r
+ Te0[(t1 >> 24) ] ^\r
+ Te1[(t2 >> 16) & 0xff] ^\r
+ Te2[(t3 >> 8) & 0xff] ^\r
+ Te3[(t0 ) & 0xff] ^\r
+ rk[1];\r
+ s2 =\r
+ Te0[(t2 >> 24) ] ^\r
+ Te1[(t3 >> 16) & 0xff] ^\r
+ Te2[(t0 >> 8) & 0xff] ^\r
+ Te3[(t1 ) & 0xff] ^\r
+ rk[2];\r
+ s3 =\r
+ Te0[(t3 >> 24) ] ^\r
+ Te1[(t0 >> 16) & 0xff] ^\r
+ Te2[(t1 >> 8) & 0xff] ^\r
+ Te3[(t2 ) & 0xff] ^\r
+ rk[3];\r
+ }\r
+#endif /* ?FULL_UNROLL */\r
+ /*\r
+ * apply last round and\r
+ * map cipher state to byte array block:\r
+ */\r
+ s0 =\r
+ (Te4[(t0 >> 24) ] & 0xff000000) ^\r
+ (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^\r
+ (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^\r
+ (Te4[(t3 ) & 0xff] & 0x000000ff) ^\r
+ rk[0];\r
+ PUTU32(out , s0);\r
+ s1 =\r
+ (Te4[(t1 >> 24) ] & 0xff000000) ^\r
+ (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^\r
+ (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^\r
+ (Te4[(t0 ) & 0xff] & 0x000000ff) ^\r
+ rk[1];\r
+ PUTU32(out + 4, s1);\r
+ s2 =\r
+ (Te4[(t2 >> 24) ] & 0xff000000) ^\r
+ (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^\r
+ (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^\r
+ (Te4[(t1 ) & 0xff] & 0x000000ff) ^\r
+ rk[2];\r
+ PUTU32(out + 8, s2);\r
+ s3 =\r
+ (Te4[(t3 >> 24) ] & 0xff000000) ^\r
+ (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^\r
+ (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^\r
+ (Te4[(t2 ) & 0xff] & 0x000000ff) ^\r
+ rk[3];\r
+ PUTU32(out + 12, s3);\r
+}\r
+\r
+/*\r
+ * Decrypt a single block\r
+ * in and out can overlap\r
+ */\r
+void AES_decrypt(const unsigned char *in, unsigned char *out,\r
+ const AES_KEY *key) {\r
+\r
+ const u32 *rk;\r
+ u32 s0, s1, s2, s3, t0, t1, t2, t3;\r
+#ifndef FULL_UNROLL\r
+ int r;\r
+#endif /* ?FULL_UNROLL */\r
+\r
+ assert(in && out && key);\r
+ rk = key->rd_key;\r
+\r
+ /*\r
+ * map byte array block to cipher state\r
+ * and add initial round key:\r
+ */\r
+ s0 = GETU32(in ) ^ rk[0];\r
+ s1 = GETU32(in + 4) ^ rk[1];\r
+ s2 = GETU32(in + 8) ^ rk[2];\r
+ s3 = GETU32(in + 12) ^ rk[3];\r
+#ifdef FULL_UNROLL\r
+ /* round 1: */\r
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4];\r
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5];\r
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6];\r
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7];\r
+ /* round 2: */\r
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8];\r
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9];\r
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10];\r
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11];\r
+ /* round 3: */\r
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12];\r
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13];\r
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14];\r
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15];\r
+ /* round 4: */\r
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16];\r
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17];\r
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18];\r
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19];\r
+ /* round 5: */\r
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20];\r
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21];\r
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22];\r
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23];\r
+ /* round 6: */\r
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24];\r
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25];\r
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26];\r
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27];\r
+ /* round 7: */\r
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28];\r
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29];\r
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30];\r
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31];\r
+ /* round 8: */\r
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32];\r
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33];\r
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34];\r
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35];\r
+ /* round 9: */\r
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36];\r
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37];\r
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38];\r
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39];\r
+ if (key->rounds > 10) {\r
+ /* round 10: */\r
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[40];\r
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[41];\r
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[42];\r
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[43];\r
+ /* round 11: */\r
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[44];\r
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[45];\r
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[46];\r
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[47];\r
+ if (key->rounds > 12) {\r
+ /* round 12: */\r
+ s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[48];\r
+ s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[49];\r
+ s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[50];\r
+ s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[51];\r
+ /* round 13: */\r
+ t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[52];\r
+ t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[53];\r
+ t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[54];\r
+ t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[55];\r
+ }\r
+ }\r
+ rk += key->rounds << 2;\r
+#else /* !FULL_UNROLL */\r
+ /*\r
+ * Nr - 1 full rounds:\r
+ */\r
+ r = key->rounds >> 1;\r
+ for (;;) {\r
+ t0 =\r
+ Td0[(s0 >> 24) ] ^\r
+ Td1[(s3 >> 16) & 0xff] ^\r
+ Td2[(s2 >> 8) & 0xff] ^\r
+ Td3[(s1 ) & 0xff] ^\r
+ rk[4];\r
+ t1 =\r
+ Td0[(s1 >> 24) ] ^\r
+ Td1[(s0 >> 16) & 0xff] ^\r
+ Td2[(s3 >> 8) & 0xff] ^\r
+ Td3[(s2 ) & 0xff] ^\r
+ rk[5];\r
+ t2 =\r
+ Td0[(s2 >> 24) ] ^\r
+ Td1[(s1 >> 16) & 0xff] ^\r
+ Td2[(s0 >> 8) & 0xff] ^\r
+ Td3[(s3 ) & 0xff] ^\r
+ rk[6];\r
+ t3 =\r
+ Td0[(s3 >> 24) ] ^\r
+ Td1[(s2 >> 16) & 0xff] ^\r
+ Td2[(s1 >> 8) & 0xff] ^\r
+ Td3[(s0 ) & 0xff] ^\r
+ rk[7];\r
+\r
+ rk += 8;\r
+ if (--r == 0) {\r
+ break;\r
+ }\r
+\r
+ s0 =\r
+ Td0[(t0 >> 24) ] ^\r
+ Td1[(t3 >> 16) & 0xff] ^\r
+ Td2[(t2 >> 8) & 0xff] ^\r
+ Td3[(t1 ) & 0xff] ^\r
+ rk[0];\r
+ s1 =\r
+ Td0[(t1 >> 24) ] ^\r
+ Td1[(t0 >> 16) & 0xff] ^\r
+ Td2[(t3 >> 8) & 0xff] ^\r
+ Td3[(t2 ) & 0xff] ^\r
+ rk[1];\r
+ s2 =\r
+ Td0[(t2 >> 24) ] ^\r
+ Td1[(t1 >> 16) & 0xff] ^\r
+ Td2[(t0 >> 8) & 0xff] ^\r
+ Td3[(t3 ) & 0xff] ^\r
+ rk[2];\r
+ s3 =\r
+ Td0[(t3 >> 24) ] ^\r
+ Td1[(t2 >> 16) & 0xff] ^\r
+ Td2[(t1 >> 8) & 0xff] ^\r
+ Td3[(t0 ) & 0xff] ^\r
+ rk[3];\r
+ }\r
+#endif /* ?FULL_UNROLL */\r
+ /*\r
+ * apply last round and\r
+ * map cipher state to byte array block:\r
+ */\r
+ s0 =\r
+ (Td4[(t0 >> 24) ] & 0xff000000) ^\r
+ (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^\r
+ (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^\r
+ (Td4[(t1 ) & 0xff] & 0x000000ff) ^\r
+ rk[0];\r
+ PUTU32(out , s0);\r
+ s1 =\r
+ (Td4[(t1 >> 24) ] & 0xff000000) ^\r
+ (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^\r
+ (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^\r
+ (Td4[(t2 ) & 0xff] & 0x000000ff) ^\r
+ rk[1];\r
+ PUTU32(out + 4, s1);\r
+ s2 =\r
+ (Td4[(t2 >> 24) ] & 0xff000000) ^\r
+ (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^\r
+ (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^\r
+ (Td4[(t3 ) & 0xff] & 0x000000ff) ^\r
+ rk[2];\r
+ PUTU32(out + 8, s2);\r
+ s3 =\r
+ (Td4[(t3 >> 24) ] & 0xff000000) ^\r
+ (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^\r
+ (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^\r
+ (Td4[(t0 ) & 0xff] & 0x000000ff) ^\r
+ rk[3];\r
+ PUTU32(out + 12, s3);\r
+}\r
+\r
+void AES_cbc_encrypt(const unsigned char *in, unsigned char *out,\r
+ const unsigned long length, const AES_KEY *key,\r
+ unsigned char *ivec, const int enc) {\r
+\r
+ unsigned long n;\r
+ unsigned long len = length;\r
+ unsigned char tmp[AES_BLOCK_SIZE];\r
+\r
+ assert(in && out && key && ivec);\r
+ assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));\r
+\r
+ if (AES_ENCRYPT == enc) {\r
+ while (len >= AES_BLOCK_SIZE) {\r
+ for(n=0; n < AES_BLOCK_SIZE; ++n)\r
+ tmp[n] = in[n] ^ ivec[n];\r
+ AES_encrypt(tmp, out, key);\r
+ memcpy(ivec, out, AES_BLOCK_SIZE);\r
+ len -= AES_BLOCK_SIZE;\r
+ in += AES_BLOCK_SIZE;\r
+ out += AES_BLOCK_SIZE;\r
+ }\r
+ if (len) {\r
+ for(n=0; n < len; ++n)\r
+ tmp[n] = in[n] ^ ivec[n];\r
+ for(n=len; n < AES_BLOCK_SIZE; ++n)\r
+ tmp[n] = ivec[n];\r
+ AES_encrypt(tmp, tmp, key);\r
+ memcpy(out, tmp, AES_BLOCK_SIZE);\r
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);\r
+ } \r
+ } else {\r
+ while (len >= AES_BLOCK_SIZE) {\r
+ memcpy(tmp, in, AES_BLOCK_SIZE);\r
+ AES_decrypt(in, out, key);\r
+ for(n=0; n < AES_BLOCK_SIZE; ++n)\r
+ out[n] ^= ivec[n];\r
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);\r
+ len -= AES_BLOCK_SIZE;\r
+ in += AES_BLOCK_SIZE;\r
+ out += AES_BLOCK_SIZE;\r
+ }\r
+ if (len) {\r
+ memcpy(tmp, in, AES_BLOCK_SIZE);\r
+ AES_decrypt(tmp, tmp, key);\r
+ for(n=0; n < len; ++n)\r
+ out[n] ^= ivec[n];\r
+ memcpy(ivec, tmp, AES_BLOCK_SIZE);\r
+ } \r
+ }\r
+}\r
+\r
+void AES_cfb128_encrypt(const unsigned char *in, unsigned char *out,\r
+ const unsigned long length, const AES_KEY *key,\r
+ unsigned char *ivec, int *num, const int enc) {\r
+\r
+ unsigned int n;\r
+ unsigned long l = length;\r
+ unsigned char c;\r
+\r
+ assert(in && out && key && ivec && num);\r
+\r
+ n = *num;\r
+\r
+ if (enc) {\r
+ while (l--) {\r
+ if (n == 0) {\r
+ AES_encrypt(ivec, ivec, key);\r
+ }\r
+ ivec[n] = *(out++) = *(in++) ^ ivec[n];\r
+ n = (n+1) % AES_BLOCK_SIZE;\r
+ }\r
+ } else {\r
+ while (l--) {\r
+ if (n == 0) {\r
+ AES_encrypt(ivec, ivec, key);\r
+ }\r
+ c = *(in);\r
+ *(out++) = *(in++) ^ ivec[n];\r
+ ivec[n] = c;\r
+ n = (n+1) % AES_BLOCK_SIZE;\r
+ }\r
+ }\r
+\r
+ *num=n;\r
+}\r
+\r
+/* increment counter (128-bit int) by 1 */\r
+static void AES_ctr128_inc(unsigned char *counter) {\r
+ unsigned long c;\r
+\r
+ /* Grab bottom dword of counter and increment */\r
+#ifdef L_ENDIAN\r
+ c = GETU32(counter + 0);\r
+ c++;\r
+ PUTU32(counter + 0, c);\r
+#else\r
+ c = GETU32(counter + 12);\r
+ c++;\r
+ PUTU32(counter + 12, c);\r
+#endif\r
+\r
+ /* if no overflow, we're done */\r
+ if (c)\r
+ return;\r
+\r
+ /* Grab 1st dword of counter and increment */\r
+#ifdef L_ENDIAN\r
+ c = GETU32(counter + 4);\r
+ c++;\r
+ PUTU32(counter + 4, c);\r
+#else\r
+ c = GETU32(counter + 8);\r
+ c++;\r
+ PUTU32(counter + 8, c);\r
+#endif\r
+\r
+ /* if no overflow, we're done */\r
+ if (c)\r
+ return;\r
+\r
+ /* Grab 2nd dword of counter and increment */\r
+#ifdef L_ENDIAN\r
+ c = GETU32(counter + 8);\r
+ c++;\r
+ PUTU32(counter + 8, c);\r
+#else\r
+ c = GETU32(counter + 4);\r
+ c++;\r
+ PUTU32(counter + 4, c);\r
+#endif\r
+\r
+ /* if no overflow, we're done */\r
+ if (c)\r
+ return;\r
+\r
+ /* Grab top dword of counter and increment */\r
+#ifdef L_ENDIAN\r
+ c = GETU32(counter + 12);\r
+ c++;\r
+ PUTU32(counter + 12, c);\r
+#else\r
+ c = GETU32(counter + 0);\r
+ c++;\r
+ PUTU32(counter + 0, c);\r
+#endif\r
+\r
+}\r
+\r
+void AES_ctr128_encrypt(const unsigned char *in, unsigned char *out,\r
+ const unsigned long length, const AES_KEY *key,\r
+ unsigned char ivec[AES_BLOCK_SIZE],\r
+ unsigned char ecount_buf[AES_BLOCK_SIZE],\r
+ unsigned int *num) {\r
+\r
+ unsigned int n;\r
+ unsigned long l=length;\r
+\r
+// assert(in && out && key && counter && num);\r
+ assert(in && out && key && num);\r
+ assert(*num < AES_BLOCK_SIZE);\r
+\r
+ n = *num;\r
+\r
+ while (l--) {\r
+ if (n == 0) {\r
+ AES_encrypt(ivec, ecount_buf, key);\r
+ AES_ctr128_inc(ivec);\r
+ }\r
+ *(out++) = *(in++) ^ ecount_buf[n];\r
+ n = (n+1) % AES_BLOCK_SIZE;\r
+ }\r
+\r
+ *num=n;\r
+}\r
+\r
+void AES_ecb_encrypt(const unsigned char *in, unsigned char *out,\r
+ const AES_KEY *key, const int enc) {\r
+\r
+ assert(in && out && key);\r
+ assert((AES_ENCRYPT == enc)||(AES_DECRYPT == enc));\r
+\r
+ if (AES_ENCRYPT == enc)\r
+ AES_encrypt(in, out, key);\r
+ else\r
+ AES_decrypt(in, out, key);\r
+}\r
+\r
+const char *AES_version="AES";\r
+\r
+const char *AES_options(void) {\r
+#ifdef FULL_UNROLL\r
+ return "aes(full)";\r
+#else \r
+ return "aes(partial)";\r
+#endif\r
+}\r
+\r
+void AES_ofb128_encrypt(const unsigned char *in, unsigned char *out,\r
+ const unsigned long length, const AES_KEY *key,\r
+ unsigned char *ivec, int *num) {\r
+\r
+ unsigned int n;\r
+ unsigned long l=length;\r
+\r
+ assert(in && out && key && ivec && num);\r
+\r
+ n = *num;\r
+\r
+ while (l--) {\r
+ if (n == 0) {\r
+ AES_encrypt(ivec, ivec, key);\r
+ }\r
+ *(out++) = *(in++) ^ ivec[n];\r
+ n = (n+1) % AES_BLOCK_SIZE;\r
+ }\r
+\r
+ *num=n;\r
+}\r
--- /dev/null
+/* crypto/idea/i_cbc.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include "idea.h"
+#include "idea_lcl.h"
+
+void idea_cbc_encrypt(const unsigned char *in, unsigned char *out, long length,
+ IDEA_KEY_SCHEDULE *ks, unsigned char *iv, int encrypt)
+ {
+ register unsigned long tin0,tin1;
+ register unsigned long tout0,tout1,xor0,xor1;
+ register long l=length;
+ unsigned long tin[2];
+
+ if (encrypt)
+ {
+ n2l(iv,tout0);
+ n2l(iv,tout1);
+ iv-=8;
+ for (l-=8; l>=0; l-=8)
+ {
+ n2l(in,tin0);
+ n2l(in,tin1);
+ tin0^=tout0;
+ tin1^=tout1;
+ tin[0]=tin0;
+ tin[1]=tin1;
+ idea_encrypt(tin,ks);
+ tout0=tin[0]; l2n(tout0,out);
+ tout1=tin[1]; l2n(tout1,out);
+ }
+ if (l != -8)
+ {
+ n2ln(in,tin0,tin1,l+8);
+ tin0^=tout0;
+ tin1^=tout1;
+ tin[0]=tin0;
+ tin[1]=tin1;
+ idea_encrypt(tin,ks);
+ tout0=tin[0]; l2n(tout0,out);
+ tout1=tin[1]; l2n(tout1,out);
+ }
+ l2n(tout0,iv);
+ l2n(tout1,iv);
+ }
+ else
+ {
+ n2l(iv,xor0);
+ n2l(iv,xor1);
+ iv-=8;
+ for (l-=8; l>=0; l-=8)
+ {
+ n2l(in,tin0); tin[0]=tin0;
+ n2l(in,tin1); tin[1]=tin1;
+ idea_encrypt(tin,ks);
+ tout0=tin[0]^xor0;
+ tout1=tin[1]^xor1;
+ l2n(tout0,out);
+ l2n(tout1,out);
+ xor0=tin0;
+ xor1=tin1;
+ }
+ if (l != -8)
+ {
+ n2l(in,tin0); tin[0]=tin0;
+ n2l(in,tin1); tin[1]=tin1;
+ idea_encrypt(tin,ks);
+ tout0=tin[0]^xor0;
+ tout1=tin[1]^xor1;
+ l2nn(tout0,tout1,out,l+8);
+ xor0=tin0;
+ xor1=tin1;
+ }
+ l2n(xor0,iv);
+ l2n(xor1,iv);
+ }
+ tin0=tin1=tout0=tout1=xor0=xor1=0;
+ tin[0]=tin[1]=0;
+ }
+
+void idea_encrypt(unsigned long *d, IDEA_KEY_SCHEDULE *key)
+ {
+ register IDEA_INT *p;
+ register unsigned long x1,x2,x3,x4,t0,t1,ul;
+
+ x2=d[0];
+ x1=(x2>>16);
+ x4=d[1];
+ x3=(x4>>16);
+
+ p= &(key->data[0][0]);
+
+ E_IDEA(0);
+ E_IDEA(1);
+ E_IDEA(2);
+ E_IDEA(3);
+ E_IDEA(4);
+ E_IDEA(5);
+ E_IDEA(6);
+ E_IDEA(7);
+
+ x1&=0xffff;
+ idea_mul(x1,x1,*p,ul); p++;
+
+ t0= x3+ *(p++);
+ t1= x2+ *(p++);
+
+ x4&=0xffff;
+ idea_mul(x4,x4,*p,ul);
+
+ d[0]=(t0&0xffff)|((x1&0xffff)<<16);
+ d[1]=(x4&0xffff)|((t1&0xffff)<<16);
+ }
--- /dev/null
+/* crypto/idea/i_skey.c */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#include "idea.h"
+#include "idea_lcl.h"
+
+static IDEA_INT inverse(unsigned int xin);
+void idea_set_encrypt_key(const unsigned char *key, IDEA_KEY_SCHEDULE *ks)
+ {
+ int i;
+ register IDEA_INT *kt,*kf,r0,r1,r2;
+
+ kt= &(ks->data[0][0]);
+ n2s(key,kt[0]); n2s(key,kt[1]); n2s(key,kt[2]); n2s(key,kt[3]);
+ n2s(key,kt[4]); n2s(key,kt[5]); n2s(key,kt[6]); n2s(key,kt[7]);
+
+ kf=kt;
+ kt+=8;
+ for (i=0; i<6; i++)
+ {
+ r2= kf[1];
+ r1= kf[2];
+ *(kt++)= ((r2<<9) | (r1>>7))&0xffff;
+ r0= kf[3];
+ *(kt++)= ((r1<<9) | (r0>>7))&0xffff;
+ r1= kf[4];
+ *(kt++)= ((r0<<9) | (r1>>7))&0xffff;
+ r0= kf[5];
+ *(kt++)= ((r1<<9) | (r0>>7))&0xffff;
+ r1= kf[6];
+ *(kt++)= ((r0<<9) | (r1>>7))&0xffff;
+ r0= kf[7];
+ *(kt++)= ((r1<<9) | (r0>>7))&0xffff;
+ r1= kf[0];
+ if (i >= 5) break;
+ *(kt++)= ((r0<<9) | (r1>>7))&0xffff;
+ *(kt++)= ((r1<<9) | (r2>>7))&0xffff;
+ kf+=8;
+ }
+ }
+
+void idea_set_decrypt_key(IDEA_KEY_SCHEDULE *ek, IDEA_KEY_SCHEDULE *dk)
+ {
+ int r;
+ register IDEA_INT *fp,*tp,t;
+
+ tp= &(dk->data[0][0]);
+ fp= &(ek->data[8][0]);
+ for (r=0; r<9; r++)
+ {
+ *(tp++)=inverse(fp[0]);
+ *(tp++)=((int)(0x10000L-fp[2])&0xffff);
+ *(tp++)=((int)(0x10000L-fp[1])&0xffff);
+ *(tp++)=inverse(fp[3]);
+ if (r == 8) break;
+ fp-=6;
+ *(tp++)=fp[4];
+ *(tp++)=fp[5];
+ }
+
+ tp= &(dk->data[0][0]);
+ t=tp[1];
+ tp[1]=tp[2];
+ tp[2]=t;
+
+ t=tp[49];
+ tp[49]=tp[50];
+ tp[50]=t;
+ }
+
+/* taken directly from the 'paper' I'll have a look at it later */
+static IDEA_INT inverse(unsigned int xin)
+ {
+ long n1,n2,q,r,b1,b2,t;
+
+ if (xin == 0)
+ b2=0;
+ else
+ {
+ n1=0x10001;
+ n2=xin;
+ b2=1;
+ b1=0;
+
+ do {
+ r=(n1%n2);
+ q=(n1-r)/n2;
+ if (r == 0)
+ { if (b2 < 0) b2=0x10001+b2; }
+ else
+ {
+ n1=n2;
+ n2=r;
+ t=b2;
+ b2=b1-q*b2;
+ b1=t;
+ }
+ } while (r != 0);
+ }
+ return((IDEA_INT)b2);
+ }
--- /dev/null
+/* crypto/idea/idea.h */
+/* Copyright (C) 1995-1997 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+#ifndef HEADER_IDEA_H
+#define HEADER_IDEA_H
+
+/*#include <openssl/opensslconf.h>*/ /* IDEA_INT, OPENSSL_NO_IDEA */
+#define IDEA_INT unsigned int
+
+/*
+#ifdef OPENSSL_NO_IDEA
+#error IDEA is disabled.
+#endif
+*/
+
+#define IDEA_ENCRYPT 1
+#define IDEA_DECRYPT 0
+
+#define IDEA_BLOCK 8
+#define IDEA_KEY_LENGTH 16
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct idea_key_st
+ {
+ IDEA_INT data[9][6];
+ } IDEA_KEY_SCHEDULE;
+
+const char *idea_options(void);
+void idea_ecb_encrypt(const unsigned char *in, unsigned char *out,
+ IDEA_KEY_SCHEDULE *ks);
+void idea_set_encrypt_key(const unsigned char *key, IDEA_KEY_SCHEDULE *ks);
+void idea_set_decrypt_key(IDEA_KEY_SCHEDULE *ek, IDEA_KEY_SCHEDULE *dk);
+void idea_cbc_encrypt(const unsigned char *in, unsigned char *out,
+ long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv,int enc);
+void idea_cfb64_encrypt(const unsigned char *in, unsigned char *out,
+ long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv,
+ int *num,int enc);
+void idea_ofb64_encrypt(const unsigned char *in, unsigned char *out,
+ long length, IDEA_KEY_SCHEDULE *ks, unsigned char *iv, int *num);
+void idea_encrypt(unsigned long *in, IDEA_KEY_SCHEDULE *ks);
+#ifdef __cplusplus
+}
+#endif
+
+#endif
--- /dev/null
+/* crypto/idea/idea_lcl.h */
+/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ *
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to. The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code. The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * "This product includes cryptographic software written by
+ * Eric Young (eay@cryptsoft.com)"
+ * The word 'cryptographic' can be left out if the rouines from the library
+ * being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from
+ * the apps directory (application code) you must include an acknowledgement:
+ * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed. i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ */
+
+/* The new form of this macro (check if the a*b == 0) was suggested by
+ * Colin Plumb <colin@nyx10.cs.du.edu> */
+/* Removal of the inner if from from Wei Dai 24/4/96 */
+#define idea_mul(r,a,b,ul) \
+ul=(unsigned long)a*b; \
+if (ul != 0) \
+ { \
+ r=(ul&0xffff)-(ul>>16); \
+ r-=((r)>>16); \
+ } \
+else \
+ r=(-(int)a-b+1); /* assuming a or b is 0 and in range */ \
+
+#ifdef undef
+#define idea_mul(r,a,b,ul,sl) \
+if (a == 0) r=(0x10001-b)&0xffff; \
+else if (b == 0) r=(0x10001-a)&0xffff; \
+else { \
+ ul=(unsigned long)a*b; \
+ sl=(ul&0xffff)-(ul>>16); \
+ if (sl <= 0) sl+=0x10001; \
+ r=sl; \
+ }
+#endif
+
+/* 7/12/95 - Many thanks to Rhys Weatherley <rweather@us.oracle.com>
+ * for pointing out that I was assuming little endian
+ * byte order for all quantities what idea
+ * actually used bigendian. No where in the spec does it mention
+ * this, it is all in terms of 16 bit numbers and even the example
+ * does not use byte streams for the input example :-(.
+ * If you byte swap each pair of input, keys and iv, the functions
+ * would produce the output as the old version :-(.
+ */
+
+/* NOTE - c is not incremented as per n2l */
+#define n2ln(c,l1,l2,n) { \
+ c+=n; \
+ l1=l2=0; \
+ switch (n) { \
+ case 8: l2 =((unsigned long)(*(--(c)))) ; \
+ case 7: l2|=((unsigned long)(*(--(c))))<< 8; \
+ case 6: l2|=((unsigned long)(*(--(c))))<<16; \
+ case 5: l2|=((unsigned long)(*(--(c))))<<24; \
+ case 4: l1 =((unsigned long)(*(--(c)))) ; \
+ case 3: l1|=((unsigned long)(*(--(c))))<< 8; \
+ case 2: l1|=((unsigned long)(*(--(c))))<<16; \
+ case 1: l1|=((unsigned long)(*(--(c))))<<24; \
+ } \
+ }
+
+/* NOTE - c is not incremented as per l2n */
+#define l2nn(l1,l2,c,n) { \
+ c+=n; \
+ switch (n) { \
+ case 8: *(--(c))=(unsigned char)(((l2) )&0xff); \
+ case 7: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+ case 6: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+ case 5: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+ case 4: *(--(c))=(unsigned char)(((l1) )&0xff); \
+ case 3: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+ case 2: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+ case 1: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+ } \
+ }
+
+#undef n2l
+#define n2l(c,l) (l =((unsigned long)(*((c)++)))<<24L, \
+ l|=((unsigned long)(*((c)++)))<<16L, \
+ l|=((unsigned long)(*((c)++)))<< 8L, \
+ l|=((unsigned long)(*((c)++))))
+
+#undef l2n
+#define l2n(l,c) (*((c)++)=(unsigned char)(((l)>>24L)&0xff), \
+ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+ *((c)++)=(unsigned char)(((l) )&0xff))
+
+#undef s2n
+#define s2n(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
+ *((c)++)=(unsigned char)(((l)>> 8L)&0xff))
+
+#undef n2s
+#define n2s(c,l) (l =((IDEA_INT)(*((c)++)))<< 8L, \
+ l|=((IDEA_INT)(*((c)++))) )
+
+#ifdef undef
+/* NOTE - c is not incremented as per c2l */
+#define c2ln(c,l1,l2,n) { \
+ c+=n; \
+ l1=l2=0; \
+ switch (n) { \
+ case 8: l2 =((unsigned long)(*(--(c))))<<24; \
+ case 7: l2|=((unsigned long)(*(--(c))))<<16; \
+ case 6: l2|=((unsigned long)(*(--(c))))<< 8; \
+ case 5: l2|=((unsigned long)(*(--(c)))); \
+ case 4: l1 =((unsigned long)(*(--(c))))<<24; \
+ case 3: l1|=((unsigned long)(*(--(c))))<<16; \
+ case 2: l1|=((unsigned long)(*(--(c))))<< 8; \
+ case 1: l1|=((unsigned long)(*(--(c)))); \
+ } \
+ }
+
+/* NOTE - c is not incremented as per l2c */
+#define l2cn(l1,l2,c,n) { \
+ c+=n; \
+ switch (n) { \
+ case 8: *(--(c))=(unsigned char)(((l2)>>24)&0xff); \
+ case 7: *(--(c))=(unsigned char)(((l2)>>16)&0xff); \
+ case 6: *(--(c))=(unsigned char)(((l2)>> 8)&0xff); \
+ case 5: *(--(c))=(unsigned char)(((l2) )&0xff); \
+ case 4: *(--(c))=(unsigned char)(((l1)>>24)&0xff); \
+ case 3: *(--(c))=(unsigned char)(((l1)>>16)&0xff); \
+ case 2: *(--(c))=(unsigned char)(((l1)>> 8)&0xff); \
+ case 1: *(--(c))=(unsigned char)(((l1) )&0xff); \
+ } \
+ }
+
+#undef c2s
+#define c2s(c,l) (l =((unsigned long)(*((c)++))) , \
+ l|=((unsigned long)(*((c)++)))<< 8L)
+
+#undef s2c
+#define s2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
+ *((c)++)=(unsigned char)(((l)>> 8L)&0xff))
+
+#undef c2l
+#define c2l(c,l) (l =((unsigned long)(*((c)++))) , \
+ l|=((unsigned long)(*((c)++)))<< 8L, \
+ l|=((unsigned long)(*((c)++)))<<16L, \
+ l|=((unsigned long)(*((c)++)))<<24L)
+
+#undef l2c
+#define l2c(l,c) (*((c)++)=(unsigned char)(((l) )&0xff), \
+ *((c)++)=(unsigned char)(((l)>> 8L)&0xff), \
+ *((c)++)=(unsigned char)(((l)>>16L)&0xff), \
+ *((c)++)=(unsigned char)(((l)>>24L)&0xff))
+#endif
+
+#define E_IDEA(num) \
+ x1&=0xffff; \
+ idea_mul(x1,x1,*p,ul); p++; \
+ x2+= *(p++); \
+ x3+= *(p++); \
+ x4&=0xffff; \
+ idea_mul(x4,x4,*p,ul); p++; \
+ t0=(x1^x3)&0xffff; \
+ idea_mul(t0,t0,*p,ul); p++; \
+ t1=(t0+(x2^x4))&0xffff; \
+ idea_mul(t1,t1,*p,ul); p++; \
+ t0+=t1; \
+ x1^=t1; \
+ x4^=t0; \
+ ul=x2^t0; /* do the swap to x3 */ \
+ x2=x3^t1; \
+ x3=ul;
+
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <ctype.h>
+
+#include <vdr/tools.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "data.h"
+#include "crypto-bn.h"
+
+// -- cHexKey ------------------------------------------------------------------
+
+cHexKey::cHexKey(bool Super)
+:cPlainKey(Super)
+{
+ key=0;
+}
+
+cHexKey::~cHexKey()
+{
+ free(key);
+}
+
+bool cHexKey::SetKey(void *Key, int Keylen)
+{
+ keylen=Keylen;
+ free(key); key=MALLOC(unsigned char,keylen);
+ if(key) memcpy(key,Key,keylen);
+ return key!=0;
+}
+
+bool cHexKey::SetBinKey(unsigned char *Mem, int Keylen)
+{
+ return SetKey(Mem,Keylen);
+}
+
+bool cHexKey::Cmp(void *Key, int Keylen)
+{
+ return keylen==Keylen && memcmp(key,Key,keylen)==0;
+}
+
+bool cHexKey::Cmp(cPlainKey *k)
+{
+ cHexKey *hk=dynamic_cast<cHexKey *>(k); // downcast
+ return hk && Cmp(hk->key,hk->keylen);
+}
+
+void cHexKey::Get(void *mem)
+{
+ memcpy(mem,key,keylen);
+}
+
+cString cHexKey::Print(void)
+{
+ char *str=(char *)malloc(keylen*2+2);
+ if(str) HexStr(str,key,keylen);
+ return cString(str,true);
+}
+
+// -- cBNKey -------------------------------------------------------------------
+
+cBNKey::cBNKey(bool Super, bool Rotate)
+:cPlainKey(Super)
+{
+ key=new cBN; rotate=Rotate;
+}
+
+cBNKey::~cBNKey()
+{
+ delete key;
+}
+
+bool cBNKey::SetKey(void *Key ,int Keylen)
+{
+ keylen=Keylen;
+ return BN_copy(*key,(BIGNUM *)Key)!=0;
+}
+
+bool cBNKey::SetBinKey(unsigned char *Mem, int Keylen)
+{
+ keylen=Keylen;
+ return rotate ? key->GetLE(Mem,Keylen) : key->Get(Mem,Keylen);
+}
+
+bool cBNKey::Cmp(void *Key, int Keylen)
+{
+ return BN_cmp(*key,(BIGNUM *)Key)==0;
+}
+
+bool cBNKey::Cmp(cPlainKey *k)
+{
+ cBNKey *bk=dynamic_cast<cBNKey *>(k); // downcast
+ return bk && BN_cmp(*key,*bk->key)==0;
+}
+
+void cBNKey::Get(void *mem)
+{
+ BN_copy((BIGNUM *)mem,*key);
+}
+
+cString cBNKey::Print(void)
+{
+ unsigned char mem[keylen];
+ if(rotate) key->PutLE(mem,keylen); else key->Put(mem,keylen);
+ char *str=(char *)malloc(keylen*2+2);
+ if(str) HexStr(str,mem,keylen);
+ return cString(str,true);
+}
+
+// -- cDualKey -----------------------------------------------------------------
+
+cDualKey::cDualKey(bool Super, bool Rotate)
+:cMutableKey(Super)
+{
+ rotate=Rotate;
+}
+
+cPlainKey *cDualKey::Alloc(void) const
+{
+ if(IsBNKey()) return new cBNKey(CanSupersede(),rotate);
+ else return new cHexKey(CanSupersede());
+}
+
+// -- cPlainKeyStd ---------------------------------------------------------------
+
+#define PLAINLEN_STD 8
+
+cPlainKeyStd::cPlainKeyStd(bool Super)
+:cHexKey(Super)
+{}
+
+bool cPlainKeyStd::Parse(const char *line)
+{
+ unsigned char sid[3], skeynr, skey[PLAINLEN_STD];
+ const char *sline=line;
+ int len;
+ if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false)) &&
+ GetHex(line,&skeynr,1) && GetHex(line,skey,PLAINLEN_STD)) {
+ type=toupper(type); id=Bin2Int(sid,len); keynr=skeynr;
+ SetBinKey(skey,PLAINLEN_STD);
+ return true;
+ }
+ FormatError("standard",sline);
+ return false;
+}
+
+// -- cSystemScCore ------------------------------------------------------------
+
+cSystemScCore::cSystemScCore(const char *Name, int Pri, int ScId, const char *ScName)
+:cSystem(Name,Pri)
+{
+ scId=ScId; scName=ScName;
+}
+
+bool cSystemScCore::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+ bool res=false;
+ cSmartCard *card=smartcards.LockCard(scId);
+ if(card) {
+ res=card->Decode(ecm,source,cw);
+ smartcards.ReleaseCard(card);
+ if(res) KeyOK(scName);
+ }
+ return res;
+}
+
+void cSystemScCore::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+ cSmartCard *card=smartcards.LockCard(scId);
+ if(card) {
+ card->Update(pid,caid,buffer);
+ smartcards.ReleaseCard(card);
+ }
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SYSTEM_COMMON_H
+#define ___SYSTEM_COMMON_H
+
+#include "data.h"
+#include "system.h"
+
+#define MBC(a,b) (((a)<<8)+(b))
+#define ADDC3(ab,c) ((ab)+((c)<<16))
+#define MBC3(a,b,c) ADDC3(MBC((a),(b)),(c))
+#define C2(x) (((x)>>8)&0xFF)
+#define C3(x) (((x)>>16)&0xFF)
+#define C2MASK 0xFFFF
+
+// ----------------------------------------------------------------
+
+class cHexKey : public cPlainKey {
+private:
+ int keylen;
+ unsigned char *key;
+protected:
+ virtual bool SetKey(void *Key, int Keylen);
+ virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+ virtual cString Print(void);
+ virtual bool Cmp(void *Key, int Keylen);
+ virtual bool Cmp(cPlainKey *k);
+ virtual void Get(void *mem);
+ virtual int Size(void) { return keylen; }
+public:
+ cHexKey(bool Super);
+ virtual ~cHexKey();
+ virtual bool Parse(const char *line) { return false; }
+ };
+
+// ----------------------------------------------------------------
+
+class cBN;
+
+class cBNKey : public cPlainKey {
+private:
+ bool rotate;
+ int keylen;
+ cBN *key;
+protected:
+ virtual bool SetKey(void *Key, int Keylen);
+ virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+ virtual cString Print(void);
+ virtual bool Cmp(void *Key, int Keylen);
+ virtual bool Cmp(cPlainKey *k);
+ virtual void Get(void *mem);
+ virtual int Size(void) { return keylen; }
+public:
+ cBNKey(bool Super, bool Rotate=false);
+ virtual ~cBNKey();
+ virtual bool Parse(const char *line) { return false; }
+ };
+
+// ----------------------------------------------------------------
+
+class cDualKey : public cMutableKey {
+private:
+ bool rotate;
+protected:
+ virtual bool IsBNKey(void) const=0;
+ virtual cPlainKey *Alloc(void) const;
+public:
+ cDualKey(bool Super, bool Rotate);
+ };
+
+// ----------------------------------------------------------------
+
+class cPlainKeyStd : public cHexKey {
+public:
+ cPlainKeyStd(bool Super);
+ virtual bool Parse(const char *line);
+ };
+
+// ----------------------------------------------------------------
+
+class cSystemScCore : public cSystem {
+private:
+ int scId;
+ const char *scName;
+public:
+ cSystemScCore(const char *Name, int Pri, int ScId, const char *ScName);
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+ virtual void ProcessEMM(int pid, int caid, unsigned char *data);
+ };
+
+#endif //___SYSTEM_COMMON_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <vdr/tools.h>
+#include <vdr/i18n.h>
+
+#include "sc.h"
+#include "scsetup.h"
+#include "system.h"
+#include "data.h"
+#include "opts.h"
+#include "log-core.h"
+
+// --- cFeature ----------------------------------------------------------------
+
+#define MAX_PHRASES 10
+
+cFeature Feature;
+
+bool cFeature::keyfile=false;
+bool cFeature::smartcard=false;
+bool cFeature::network=false;
+int cFeature::pcount=0;
+const tI18nPhrase *cFeature::phrases[MAX_PHRASES];
+
+void cFeature::AddPhrases(const tI18nPhrase * const Phrases)
+{
+ if(pcount<MAX_PHRASES)
+ phrases[pcount++]=Phrases;
+ else
+ PRINTF(L_GEN_ERROR,"feature: too many phrases");
+}
+
+tI18nPhrase *cFeature::GetPhrases(void)
+{
+ int num=1;
+ for(int i=0; i<pcount; i++) {
+ const tI18nPhrase *p=phrases[i];
+ for(; **p; p++) num++;
+ }
+ tI18nPhrase *all=MALLOC(tI18nPhrase,num);
+ if(all) {
+ tI18nPhrase *a=all;
+ for(int i=0; i<pcount; i++) {
+ const tI18nPhrase *p=phrases[i];
+ while(**p) {
+ memcpy(a,p,sizeof(tI18nPhrase));
+ a++; p++;
+ }
+ }
+ memset(a,0,sizeof(tI18nPhrase));
+ }
+ return all;
+}
+
+void cFeature::NeedsKeyFile(void)
+{
+ if(!keyfile) PRINTF(L_CORE_DYN,"feature: using feature KEYFILE");
+ keyfile=true;
+}
+
+void cFeature::NeedsSmartCard(void)
+{
+ if(!smartcard) PRINTF(L_CORE_DYN,"feature: using feature SMARTCARD");
+ smartcard=true;
+}
+
+// -- cKeySnoop ----------------------------------------------------------------
+
+cKeySnoop::cKeySnoop(cSystem *Sys, int Type, int Id, int Keynr)
+{
+ ok=false;
+ sys=Sys; type=Type; id=Id; keynr=Keynr;
+}
+
+cKeySnoop::~cKeySnoop()
+{
+ if(!ok) sys->KeyFail(type,id,keynr);
+}
+
+void cKeySnoop::OK(cPlainKey *pk)
+{
+ sys->KeyOK(pk);
+ ok=true;
+}
+
+// -- cLogHook -----------------------------------------------------------------
+
+cLogHook::cLogHook(int Id, const char *Name)
+{
+ id=Id; name=Name;
+ bailOut=false;
+}
+
+// -- cSystem ------------------------------------------------------------------
+
+#define MAX_CHID 10
+
+struct EcmCheck {
+ union {
+ struct {
+ bool startFlag;
+ int current, chids[MAX_CHID];
+ } sys06;
+ } caid;
+ };
+
+int cSystem::foundKeys=0;
+int cSystem::newKeys=0;
+
+cSystem::cSystem(const char *Name, int Pri)
+{
+ name=Name; pri=Pri;
+ lastType=0; currentKeyStr[0]=0; doLog=true; cardNum=-1; logecm=0;
+ check=new struct EcmCheck;
+ memset(check,0,sizeof(struct EcmCheck));
+ // default config
+ maxEcmTry=2; // try to get a key X times from the same ECM pid (default)
+ hasLogger=false;
+ needsLogger=false;
+ local=true;
+ needsDescrData=false;
+ constant=false;
+}
+
+cSystem::~cSystem()
+{
+ delete check;
+ if(logecm) cSoftCAM::SetLogStatus(cardNum,logecm,false);
+ delete logecm;
+}
+
+void cSystem::StartLog(const cEcmInfo *ecm, int caid)
+{
+ if(!logecm || logecm->caId!=caid) {
+ if(logecm) cSoftCAM::SetLogStatus(cardNum,logecm,false);
+ delete logecm;
+ logecm=new cEcmInfo(ecm);
+ logecm->caId=caid; logecm->emmCaId=0;
+ cSoftCAM::SetLogStatus(cardNum,logecm,true);
+ }
+}
+
+void cSystem::ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, const unsigned char *data, int len)
+{
+ const int pid=WORD(data,2,0x1FFF);
+ switch(sysId>>8) {
+ case 0x01: // Seca style
+ for(int p=2; p<len; p+=15) {
+ cEcmInfo *n=new cEcmInfo(name,WORD(data,p,0x1FFF),sysId,WORD(data,p+2,0xFFFF));
+ if(data[p+4]==0xFF) n->AddData(&data[p+5],10);
+ ecms->Add(n);
+ }
+ break;
+ case 0x05: // Viaccess style
+ for(int p=4; p<len; p+=2+data[p+1])
+ if(data[p]==0x14)
+ ecms->Add(new cEcmInfo(name,pid,sysId,(data[p+2]<<16)|(data[p+3]<<8)|(data[p+4]&0xF0)));
+ break;
+ default: // default style
+ {
+ cEcmInfo *n=new cEcmInfo(name,pid,sysId,0);
+ if(sysId==0x1234) { // BEV
+ n->ecm_table=0x8e;
+ n->emmCaId=0x1801;
+ }
+ ecms->Add(n);
+ break;
+ }
+ }
+}
+
+void cSystem::ParseCAT(cPids *pids, const unsigned char *buffer)
+{
+ if(buffer[0]==0x09) {
+ int caid=WORD(buffer,2,0xFFFF);
+ int pid=WORD(buffer,4,0x1FFF);
+ switch(caid>>8) {
+ case 0x01: // Seca style (82/84)
+ if(buffer[1]>4) {
+ pids->AddPid(pid,0x82,0xFF); // Unique updates
+ for(int i=7, nn=buffer[6] ; nn ; nn--,i+=4)
+ pids->AddPid(WORD(buffer,i,0x1FFF),0x84,0xFF); // Shared updates
+ }
+ break;
+ case 0x05: // Viaccess style (88/8c/8d/8e)
+ pids->AddPid(pid,0x8B,0xFE,0x07); // mismatching 89/8f
+ break;
+ case 0x0d: // Cryptoworks style (82/84/86/88/89)
+ pids->AddPid(pid,0x80,0xFF,0x06);
+ pids->AddPid(pid,0x88,0xFE);
+ break;
+ case 0x18: // Nagra style, Nagra1(82) Nagra2(82/83)
+ pids->AddPid(pid,0x82,caid==0x1801 ? 0xFE:0xFF);
+ break;
+ default: // default style (82)
+ pids->AddPid(pid,0x82,0xFF);
+ break;
+ }
+ }
+}
+
+int cSystem::CheckECM(const cEcmInfo *ecm, const unsigned char *data, bool sync)
+{
+ switch(ecm->caId>>8) {
+ case 0x06: // Irdeto
+ {
+ const int cur=data[4];
+ const int max=data[5];
+ const int chid=WORD(data,6,0xFFFF);
+ // if multiple channel id's, use current one only
+ if(sync && ecm->caId==0x0604 && check->caid.sys06.current>0 && chid!=check->caid.sys06.current) {
+ PRINTF(L_CORE_ECMPROC,"ecmcheck(%s): chid %04x != current %04x",name,chid,check->caid.sys06.current);
+ return 2;
+ }
+ // search for fake channel id's on Stream
+ if(!sync && max>0 && max<MAX_CHID) {
+ if(cur==0) check->caid.sys06.startFlag=true;
+ if(check->caid.sys06.startFlag) {
+ if(cur<=max) check->caid.sys06.chids[cur]=chid;
+ if(cur==max) {
+ for(int i=0 ; i<max ; i++) {
+ if(check->caid.sys06.chids[i]==0) {
+ check->caid.sys06.startFlag=false;
+ PRINTF(L_CORE_ECMPROC,"ecmcheck(%s): zero chid",name);
+ return 1;
+ }
+ for(int j=i+1 ; j<=max ; j++) {
+ if(check->caid.sys06.chids[i]==check->caid.sys06.chids[j]) {
+ check->caid.sys06.startFlag=false;
+ PRINTF(L_CORE_ECMPROC,"ecmcheck(%s): duplicate chid %04x",name,check->caid.sys06.chids[i]);
+ return 1;
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+void cSystem::CheckECMResult(const cEcmInfo *ecm, const unsigned char *data, bool result)
+{
+ switch(ecm->caId>>8) {
+ case 0x06: // Irdeto
+ check->caid.sys06.current=result ? WORD(data,6,0xFFFF) : 0;
+ break;
+ }
+}
+
+void cSystem::KeyOK(cPlainKey *pk)
+{
+ if(pk->type!=lastType || pk->id!=lastId || pk->keynr!=lastKeynr) {
+ lastType=pk->type; lastId=pk->id; lastKeynr=pk->keynr;
+ strn0cpy(currentKeyStr,pk->ToString(),sizeof(currentKeyStr));
+ PRINTF(L_CORE_ECM,"system: using key %s",*pk->ToString(true));
+ doLog=true;
+ }
+}
+
+void cSystem::KeyOK(const char *txt)
+{
+ snprintf(currentKeyStr,sizeof(currentKeyStr),"%s (%s)\n",txt?txt:name,tr("undisclosed key"));
+ doLog=true;
+}
+
+void cSystem::KeyFail(int type, int id, int keynr)
+{
+ if(type!=lastType || id!=lastId || keynr!=lastKeynr) {
+ lastType=type; lastId=id; lastKeynr=keynr;
+ if(doLog)
+ PRINTF(L_CORE_ECM,"system: no key found for %c %.2x %.2x",lastType,lastId,lastKeynr);
+ }
+}
+
+// -- cSystemLink --------------------------------------------------------------
+
+cSystemLink::cSystemLink(const char *Name, int Pri)
+{
+ name=Name; pri=Pri;
+ opts=0; noFF=false;
+ cSystems::Register(this);
+}
+
+cSystemLink::~cSystemLink()
+{
+ delete opts;
+}
+
+// -- cSystems -----------------------------------------------------------------
+
+cSystemLink *cSystems::first=0;
+int cSystems::nextSysIdent=0x1000;
+
+void cSystems::Register(cSystemLink *sysLink)
+{
+ PRINTF(L_CORE_DYN,"systems: registering CA system %s, pri %d, ident %04X",sysLink->name,sysLink->pri,nextSysIdent);
+ sysLink->next=first;
+ sysLink->sysIdent=nextSysIdent++;
+ first=sysLink;
+}
+
+int cSystems::Provides(const unsigned short *SysIds, bool ff)
+{
+ int n=0;
+ for(; *SysIds; SysIds++) {
+ if(ScSetup.Ignore(*SysIds)) continue;
+ cSystemLink *sl=first;
+ while(sl) {
+ if(sl->CanHandle(*SysIds)) {
+ if(sl->noFF && ff) return 0;
+ n++;
+ break;
+ }
+ sl=sl->next;
+ }
+ }
+ return n;
+}
+
+cSystemLink *cSystems::FindByName(const char *Name)
+{
+ cSystemLink *sl=first;
+ while(sl) {
+ if(!strcasecmp(sl->name,Name)) return sl;
+ sl=sl->next;
+ }
+ return 0;
+}
+
+cSystemLink *cSystems::FindById(unsigned short SysId, bool ff, int oldPri)
+{
+ // all pri's are negative!
+ // oldPri = 0 -> get highest pri system
+ // oldPri < 0 -> get highest pri system with pri<oldPri
+ // oldPri > 0 -> get lowest pri system
+
+ cSystemLink *sl=first, *csl=0;
+ while(sl) {
+ if((!ff || !sl->noFF) && sl->CanHandle(SysId)
+ && sl->pri<oldPri
+ && (!csl
+ || (oldPri<=0 && sl->pri>csl->pri)
+ || (oldPri>0 && sl->pri<csl->pri)
+ )
+ ) csl=sl;
+ sl=sl->next;
+ }
+ return csl;
+}
+
+cSystemLink *cSystems::FindByIdent(int ident)
+{
+ cSystemLink *sl=first;
+ while(sl) {
+ if(sl->sysIdent==ident) return sl;
+ sl=sl->next;
+ }
+ return 0;
+}
+
+cSystem *cSystems::FindBySysName(unsigned short SysId, bool ff, const char *Name)
+{
+ cSystemLink *sl=FindByName(Name);
+ if(sl && (!ff || !sl->noFF) && !ScSetup.Ignore(SysId) && sl->CanHandle(SysId)) return sl->Create();
+ return 0;
+}
+
+cSystem *cSystems::FindBySysId(unsigned short SysId, bool ff, int oldPri)
+{
+ if(!ScSetup.Ignore(SysId)) {
+ cSystemLink *csl=FindById(SysId,ff,oldPri);
+ if(csl) return csl->Create();
+ }
+ return 0;
+}
+
+int cSystems::FindIdentBySysId(unsigned short SysId, bool ff, int &Pri)
+{
+ if(!ScSetup.Ignore(SysId)) {
+ cSystemLink *csl=FindById(SysId,ff,Pri);
+ if(csl) {
+ Pri=csl->pri;
+ return csl->sysIdent;
+ }
+ }
+ return 0;
+}
+
+cSystem *cSystems::FindBySysIdent(int ident)
+{
+ cSystemLink *csl=FindByIdent(ident);
+ return csl ? csl->Create() : 0;
+}
+
+bool cSystems::Init(const char *cfgdir)
+{
+ PRINTF(L_CORE_LOAD,"** registered systems:");
+ for(cSystemLink *sl=first; sl; sl=sl->next)
+ PRINTF(L_CORE_LOAD,"** %-16s (pri %3d)",sl->name,sl->pri);
+ for(cSystemLink *sl=first; sl; sl=sl->next)
+ if(!sl->Init(cfgdir)) return false;
+ return true;
+}
+
+void cSystems::Clean(void)
+{
+ for(cSystemLink *sl=first; sl; sl=sl->next) sl->Clean();
+}
+
+bool cSystems::ConfigParse(const char *Name, const char *Value)
+{
+ char sysName[32];
+ unsigned int i;
+ for(i=0; Name[i] && Name[i]!='.' && i<sizeof(sysName); i++)
+ sysName[i]=Name[i];
+ if(Name[i]) {
+ sysName[i]=0; i++;
+ cSystemLink *sl=FindByName(sysName);
+ if(sl && sl->opts) return sl->opts->Parse(&Name[i],Value);
+ }
+ return false;
+}
+
+void cSystems::ConfigStore(bool AsIs)
+{
+ for(cSystemLink *sl=first; sl; sl=sl->next)
+ if(sl->opts && sl->opts->Store(AsIs)) sl->NewConfig();
+}
+
+cOpts *cSystems::GetSystemOpts(bool start)
+{
+ static cSystemLink *sl=0;
+ sl=(start || !sl) ? first : sl->next;
+ while(sl) {
+ if(sl->opts) return sl->opts;
+ sl=sl->next;
+ }
+ return 0;
+}
+
+// -- cMsgCache ----------------------------------------------------------------
+
+#define FREE 0x00 // modes
+#define FAIL1 0x01
+#define FAIL2 0x3D
+#define FAILN 0x3E
+#define GOOD 0x3F
+#define MASK 0x3F
+#define QUEUED 0x40
+#define WAIT 0x80
+
+struct Cache {
+ int crc;
+ int mode;
+ };
+
+cMsgCache::cMsgCache(int NumCache, int StoreSize)
+{
+ numCache=NumCache;
+ storeSize=StoreSize;
+ ptr=0; stores=0; maxFail=2;
+ caches=MALLOC(struct Cache,numCache);
+ if(caches) {
+ Clear();
+ if(storeSize>0) {
+ stores=MALLOC(unsigned char,numCache*storeSize);
+ if(!stores) PRINTF(L_GEN_ERROR,"msgcache: no memory for store area");
+ }
+ }
+ else PRINTF(L_GEN_ERROR,"msgcache: no memory for cache");
+}
+
+cMsgCache::~cMsgCache()
+{
+ free(caches);
+ free(stores);
+}
+
+void cMsgCache::SetMaxFail(int maxfail)
+{
+ maxFail=min(maxfail,FAILN);
+ PRINTF(L_CORE_MSGCACHE,"%d/%p: maxFail set to %d",getpid(),this,maxFail);
+}
+
+void cMsgCache::Clear(void)
+{
+ cMutexLock lock(&mutex);
+ memset(caches,0,sizeof(struct Cache)*numCache);
+ ptr=0;
+ PRINTF(L_CORE_MSGCACHE,"%d/%p: clear",getpid(),this);
+}
+
+struct Cache *cMsgCache::FindMsg(int crc)
+{
+ int i=ptr;
+ while(1) {
+ if(--i<0) i=numCache-1;
+ struct Cache * const s=&caches[i];
+ if(!s->mode) break;
+ if(s->crc==crc) return s;
+ if(i==ptr) break;
+ }
+ return 0;
+}
+
+// returns:
+// -1 - msg cached as failed (max failed)
+// 0 - msg cached as good, result stored
+// >0 - msg not cached, queue id
+int cMsgCache::Get(const unsigned char *msg, int len, unsigned char *store)
+{
+ int crc=crc32_le(0,msg,len);
+ cMutexLock lock(&mutex);
+ if(!caches || (storeSize>0 && !stores)) return -1; // sanity
+ struct Cache *s;
+ while((s=FindMsg(crc))) {
+ if(!(s->mode&QUEUED)) break;
+ s->mode|=WAIT;
+ PRINTF(L_CORE_MSGCACHE,"%d/%p: msg already queued. waiting to complete",getpid(),this);
+ wait.Wait(mutex);
+ }
+ int id;
+ if(!s) {
+ while(1) {
+ s=&caches[ptr];
+ if(!(s->mode&QUEUED)) break;
+ s->mode|=WAIT;
+ PRINTF(L_CORE_MSGCACHE,"%d/%p: queue overwrite protection id=%d",getpid(),this,ptr+1);
+ wait.Wait(mutex); // don't overwrite queued msg's
+ }
+ id=ptr+1;
+ s->crc=crc;
+ s->mode=QUEUED;
+ PRINTF(L_CORE_MSGCACHE,"%d/%p: queued msg with id=%d",getpid(),this,id);
+ ptr++; if(ptr>=numCache) { ptr=0; PRINTF(L_CORE_MSGCACHE,"msgcache: roll-over (%d)",numCache); }
+ return id;
+ }
+ else {
+ id=(s-&caches[0])+1;
+ switch(s->mode) {
+ case GOOD:
+ if(store && storeSize>0)
+ memcpy(store,&stores[(id-1)*storeSize],storeSize);
+ PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as GOOD (%d)",getpid(),this,id);
+ return 0;
+ case FAIL1...FAIL2:
+ PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as FAIL%d (%d)",getpid(),this,s->mode,id);
+ s->mode|=QUEUED;
+ return id;
+ case FAILN:
+ default:
+ PRINTF(L_CORE_MSGCACHE,"%d/%p: msg is cached as FAILN (%d)",getpid(),this,id);
+ return -1;
+ }
+ }
+}
+
+int cMsgCache::Cache(int id, bool result, const unsigned char *store)
+{
+ cMutexLock lock(&mutex);
+ if(id<1 || !caches || (storeSize>0 && !stores)) return 0; // sanity
+ struct Cache *s=&caches[id-1];
+ LBSTARTF(L_CORE_MSGCACHE);
+ LBPUT("%d/%p: de-queued msg with id=%d ",getpid(),this,id);
+ if(s->mode&WAIT) wait.Broadcast();
+ if(result) {
+ if(store && storeSize>0)
+ memcpy(&stores[(id-1)*storeSize],store,storeSize);
+ s->mode=GOOD;
+ LBPUT("(GOOD)");
+ return 0;
+ }
+ else {
+ switch(s->mode&MASK) {
+ case GOOD:
+ s->mode=FREE;
+ // fall through
+ case FREE:
+ case FAIL1...FAIL2:
+ s->mode=(s->mode&MASK)+1;
+ if(s->mode<maxFail) {
+ LBPUT("(FAIL%d)",s->mode);
+ break;
+ }
+ // fall through
+ case FAILN:
+ s->mode=FAILN;
+ LBPUT("(FAILN)");
+ break;
+ }
+ return s->mode;
+ }
+ LBEND();
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SYSTEM_H
+#define ___SYSTEM_H
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include <vdr/tools.h>
+#include <vdr/i18n.h>
+
+#include "data.h"
+#include "misc.h"
+#include "log-core.h"
+
+// ----------------------------------------------------------------
+
+#define SYSTEM_MASK 0xFF00
+
+// ----------------------------------------------------------------
+
+class cEcmInfo;
+class cPlainKey;
+class cSystem;
+class cSystems;
+class cLogger;
+class cOpts;
+class cHookManager;
+
+// ----------------------------------------------------------------
+
+class cFeature {
+private:
+ static bool keyfile, smartcard, network;
+ static int pcount;
+ static const tI18nPhrase *phrases[];
+public:
+ void AddPhrases(const tI18nPhrase * const Phrases);
+ tI18nPhrase *GetPhrases(void);
+ void NeedsKeyFile(void);
+ void NeedsSmartCard(void);
+ bool KeyFile(void) const { return keyfile; }
+ bool SmartCard(void) const { return smartcard; }
+ };
+
+extern cFeature Feature;
+
+// ----------------------------------------------------------------
+
+class cKeySnoop {
+private:
+ cSystem *sys;
+ int type, id, keynr;
+ bool ok;
+public:
+ cKeySnoop(cSystem *Sys, int Type, int Id, int Keynr);
+ ~cKeySnoop();
+ void OK(cPlainKey *pk);
+ };
+
+// ----------------------------------------------------------------
+
+class cLogHook : public cSimpleItem {
+friend class cHookManager;
+private:
+ int cardNum, id;
+ const char *name;
+ cTimeMs delay;
+ bool bailOut;
+protected:
+ cPids pids;
+public:
+ cLogHook(int Id, const char *Name);
+ virtual ~cLogHook() {}
+ virtual void Process(int pid, unsigned char *data)=0;
+ void BailOut(void) { bailOut=true; }
+ };
+
+// ----------------------------------------------------------------
+
+struct EcmCheck;
+
+class cSystem : public cSimpleItem {
+friend class cKeySnoop;
+private:
+ static int foundKeys, newKeys;
+ int pri, cardNum;
+ int lastType, lastId, lastKeynr;
+ char *lastTxt;
+ char currentKeyStr[48];
+ struct EcmCheck *check;
+ cEcmInfo *logecm;
+protected:
+ const char *name;
+ unsigned char cw[16];
+ bool doLog;
+ // config details
+ int maxEcmTry;
+ bool local, hasLogger, needsLogger, needsDescrData, constant;
+ //
+ void KeyOK(cPlainKey *pk);
+ void KeyOK(const char *txt);
+ void KeyFail(int type, int id, int keynr);
+ void StartLog(const cEcmInfo *ecm, int caid);
+public:
+ cSystem(const char *Name, int Pri);
+ virtual ~cSystem();
+ virtual int CheckECM(const cEcmInfo *ecm, const unsigned char *data, bool sync);
+ virtual void CheckECMResult(const cEcmInfo *ecm, const unsigned char *data, bool result);
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *buffer)=0;
+ virtual void ProcessEMM(int pid, int caid, unsigned char *buffer) {};
+ virtual void ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, const unsigned char *data, int len);
+ virtual void ParseCAT(cPids *pids, const unsigned char *buffer);
+ unsigned char *CW(void) { return cw; }
+ void DoLog(bool Log) { doLog=Log; }
+ int Pri(void) { return pri; }
+ const char *CurrentKeyStr(void) { return currentKeyStr; }
+ const char *Name(void) { return name; }
+ int CardNum(void) { return cardNum; }
+ void CardNum(int CardNum) { cardNum=CardNum; }
+ int MaxEcmTry(void) { return maxEcmTry; }
+ bool HasLogger(void) { return hasLogger; }
+ bool NeedsLogger(void) { return needsLogger; }
+ bool Local(void) { return local; }
+ bool NeedsData(void) { return needsDescrData; }
+ bool Constant(void) { return constant; }
+ static void FoundKey(void) { foundKeys++; }
+ static void NewKey(void) { newKeys++; }
+ static void KeyStats(int &fk, int &nk) { fk=foundKeys; nk=newKeys; }
+ };
+
+// ----------------------------------------------------------------
+
+class cSystemLink {
+friend class cSystems;
+private:
+ int sysIdent;
+protected:
+ cSystemLink *next;
+ const char *name;
+ int pri;
+ cOpts *opts;
+ bool noFF;
+public:
+ cSystemLink(const char *Name, int Pri);
+ virtual ~cSystemLink();
+ virtual bool CanHandle(unsigned short SysId)=0;
+ virtual cSystem *Create(void)=0;
+ virtual bool Init(const char *cfgdir) { return true; }
+ virtual void Clean(void) {}
+ virtual void NewConfig(void) {}
+ };
+
+// ----------------------------------------------------------------
+
+class cSystems {
+friend class cSystemLink;
+private:
+ static cSystemLink *first;
+ static int nextSysIdent;
+ //
+ static void Register(cSystemLink *sysLink);
+ static cSystemLink *FindByName(const char *Name);
+ static cSystemLink *FindById(unsigned short SysId, bool ff, int oldPri);
+ static cSystemLink *FindByIdent(int ident);
+public:
+ static int Provides(const unsigned short *SysIds, bool ff);
+ static cSystem *FindBySysId(unsigned short SysId, bool ff, int oldPri);
+ static cSystem *FindBySysName(unsigned short SysId, bool ff, const char *Name);
+ static int FindIdentBySysId(unsigned short SysId, bool ff, int &Pri);
+ static cSystem *FindBySysIdent(int ident);
+ static bool Init(const char *cfgdir);
+ static void Clean(void);
+ //
+ static bool ConfigParse(const char *Name, const char *Value);
+ static void ConfigStore(bool AsIs);
+ static cOpts *GetSystemOpts(bool start);
+ };
+
+// ----------------------------------------------------------------
+
+template<class T> class cCardInfos : public cSimpleList<T>, public cLoader, cConfRead {
+private:
+ const char *sysname;
+public:
+ cCardInfos(const char *id):cLoader(id) {}
+ virtual ~cCardInfos() {}
+ bool HaveCards(void) { return (this->Count()!=0); }
+ bool Load(const char *cfgdir, const char *SysName, const char *kidName)
+ {
+ sysname=SysName;
+ this->Clear();
+ char type[32];
+ snprintf(type,sizeof(type),"%s card infos",SysName);
+ cString cname=AddDirectory(cfgdir,kidName);
+ ConfRead(type,cname);
+ PRINTF(L_CORE_LOAD,"loaded %d %s cards from %s",this->Count(),SysName,*cname);
+ return HaveCards();
+ }
+ virtual bool ParseLine(const char *line, bool fromCache)
+ {
+ T *k=new T;
+ if(k) {
+ if(k->Parse((char *)line)) {
+ if(!fromCache) { Add(k); k=0; }
+ else {
+ T *o=this->First();
+ while(o) {
+ if(o->Cmp(k)) {
+ Add(k,o); k->Updated(); k=0;
+ Del(o);
+ break;
+ }
+ o=this->Next(o);
+ }
+ }
+ }
+ delete k;
+ return true;
+ }
+ PRINTF(L_GEN_ERROR,"not enough memory for %s card infos!",sysname);
+ return false;
+ }
+ virtual bool Save(FILE *f)
+ {
+ bool res=true;
+ T *k=this->First();
+ while(k) {
+ if(k->IsUpdated() && !k->Save(f)) { res=false; break; }
+ k=this->Next(k);
+ }
+ Modified(!res);
+ return res;
+ }
+ };
+
+// ----------------------------------------------------------------
+
+struct Cache;
+
+class cMsgCache {
+private:
+ struct Cache *caches;
+ unsigned char *stores;
+ int numCache, ptr, storeSize, maxFail;
+ cMutex mutex;
+ cCondVar wait;
+ //
+ struct Cache *FindMsg(int crc);
+public:
+ cMsgCache(int NumCache, int StoreSize);
+ ~cMsgCache();
+ int Cache(int id, bool result, const unsigned char *store);
+ int Get(const unsigned char *msg, int len, unsigned char *store);
+ void Clear(void);
+ void SetMaxFail(int max);
+ };
+
+#endif //___SYSTEM_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "cc.h"
+#include "network.h"
+#include "parse.h"
+
+// -- cCardClientAroureos ------------------------------------------------------
+
+class cCardClientAroureos : public cCardClient, protected cIdSet {
+private:
+ cNetSocket so;
+ //
+ bool ParseCardConfig(const char *config, int *num);
+protected:
+ virtual bool Login(void);
+public:
+ cCardClientAroureos(const char *Name);
+ virtual bool Init(const char *config);
+ virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw);
+ virtual bool ProcessEMM(int caSys, const unsigned char *source);
+ };
+
+static cCardClientLinkReg<cCardClientAroureos> __aroureos("Aroureos");
+
+cCardClientAroureos::cCardClientAroureos(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,5,DEFAULT_IDLE_TIMEOUT)
+{}
+
+bool cCardClientAroureos::ParseCardConfig(const char *config, int *num)
+{
+ int hb, hs;
+ int startNum=*num;
+ if(sscanf(&config[*num],":%x:%x%n",&hb,&hs,num)==2) {
+ *num+=startNum;
+ unsigned char h[3];
+ h[0]=(hs>>16)&0xFF;
+ h[1]=(hs>> 8)&0xFF;
+ h[2]=(hs>> 0)&0xFF;
+ PRINTF(L_CC_LOGIN,"%s: hexser %02X%02X%02X hexbase %02X",name,h[0],h[1],h[2],hb);
+ ResetIdSet();
+ SetCard(new cCardIrdeto(hb,&h[0]));
+ }
+ return true;
+}
+
+bool cCardClientAroureos::Init(const char *config)
+{
+ cMutexLock lock(this);
+ so.Disconnect();
+ int num=0;
+ return ParseStdConfig(config,&num) &&
+ ParseCardConfig(config,&num);
+}
+
+bool cCardClientAroureos::Login(void)
+{
+ so.Disconnect();
+ if(!so.Connect(hostname,port)) return false;
+ PRINTF(L_CC_LOGIN,"%s: connected to %s:%d",name,hostname,port);
+ if(!emmAllowed) PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+ return true;
+}
+
+bool cCardClientAroureos::ProcessEMM(int caSys, const unsigned char *source)
+{
+ if(emmAllowed) {
+ cMutexLock lock(this);
+ if(MatchEMM(source)) {
+ const int length=SCT_LEN(source);
+ int id=msEMM.Get(source,length,0);
+ if(id>0) {
+ unsigned char buff[length+8];
+ memcpy(buff,"EMM",3);
+ memcpy(&buff[3],source,length);
+ SendMsg(&so,buff,length+3);
+ msEMM.Cache(id,true,0);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool cCardClientAroureos::ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw)
+{
+ cMutexLock lock(this);
+ so.Flush();
+ const int len=SCT_LEN(source);
+ if(len<=93) {
+ unsigned char buff[128];
+ memcpy(buff,"ECM",3);
+ memcpy(&buff[3],source,len);
+
+ if(!SendMsg(&so,buff,96)) return false;
+ int n=RecvMsg(&so,buff,sizeof(buff));
+ if(n>0) {
+ memcpy(cw,buff,16);
+ for(n=0; n<16; n++) if(cw[n]) break;
+ if(n<16) return true;
+ }
+ }
+ return false;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <byteswap.h>
+
+#include "cc.h"
+#include "network.h"
+#include "crypto.h"
+#include "misc.h"
+#include "parse.h"
+
+#include <openssl/md5.h>
+
+#define CCVERSION "3.37"
+#define CCTIMEOUT 5000 // ms
+
+// -- cCardClientCommon --------------------------------------------------------
+
+class cCardClientCommon : public cCardClient, public cAES, protected cIdSet {
+private:
+ bool conReply, logReply, doAES;
+ bool exclusive;
+ int minMsgLen;
+ cCondVar sleepCond;
+ cTimeMs time;
+protected:
+ cNetSocket so;
+ bool emmProcessing;
+ char username[11], password[11];
+ int CAID;
+ unsigned char lastEmmReq[32];
+ //
+ virtual bool Login(void);
+ bool ParseKeyConfig(const char *config, int *num);
+ bool ParseUserConfig(const char *config, int *num);
+ virtual bool SendMsg(cNetSocket *so, const unsigned char *data, int len);
+ virtual int RecvMsg(cNetSocket *so, unsigned char *data, int len, int to=-1);
+ void HandleEMMRequest(const unsigned char *buff, int len);
+public:
+ cCardClientCommon(const char *Name, bool ConReply, bool LogReply, bool DoAES, int MinMsgLen);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual bool Init(const char *config);
+ virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw);
+ virtual bool ProcessEMM(int caSys, const unsigned char *source);
+ };
+
+cCardClientCommon::cCardClientCommon(const char *Name, bool ConReply, bool LogReply, bool DoAES, int MinMsgLen)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,CCTIMEOUT/1000,DEFAULT_IDLE_TIMEOUT)
+{
+ conReply=ConReply; logReply=LogReply; doAES=DoAES; minMsgLen=MinMsgLen;
+ emmProcessing=exclusive=false;
+ CAID=0;
+ memset(lastEmmReq,0,sizeof(lastEmmReq));
+}
+
+bool cCardClientCommon::ParseUserConfig(const char *config, int *num)
+{
+ int startNum=*num;
+ if(sscanf(&config[*num],":%10[^:]:%10[^:]%n",username,password,num)==2) {
+ *num+=startNum;
+ PRINTF(L_CC_CORE,"%s: username=%s password=%s",name,username,password);;
+ return true;
+ }
+ return false;
+}
+
+bool cCardClientCommon::ParseKeyConfig(const char *config, int *num)
+{
+ char hexkey[33];
+ int startNum=*num;
+ if(sscanf(&config[*num],":%32[^:]%n",hexkey,num)==1) {
+ *num+=startNum;
+ PRINTF(L_CC_CORE,"%s: key=%s",name,hexkey);
+ unsigned char binkey[16];
+ memset(binkey,0,sizeof(binkey));
+ const char *line=hexkey;
+ int n=GetHex(line,binkey,sizeof(binkey),false);
+ if(n!=(int)sizeof(binkey))
+ PRINTF(L_CC_CAMD,"warning AES key not %d bytes long",(int)sizeof(binkey));
+ LDUMP(L_CC_CAMD,binkey,16,"AES activated key =");
+ SetKey(binkey);
+ }
+ return true;
+}
+
+bool cCardClientCommon::SendMsg(cNetSocket *so, const unsigned char *data, int len)
+{
+ unsigned char buff2[minMsgLen];
+ if(len<minMsgLen) {
+ memcpy(buff2,data,len);
+ memset(buff2+len,0,minMsgLen-len);
+ data=buff2; len=minMsgLen;
+ }
+ unsigned char buff[len+16];
+ const int l=Encrypt(data,len,buff);
+ if(l>0) { data=buff; len=l; }
+ return cCardClient::SendMsg(so,data,len);
+}
+
+int cCardClientCommon::RecvMsg(cNetSocket *so, unsigned char *data, int len, int to)
+{
+ int n=cCardClient::RecvMsg(so,data,len,to);
+ if(n>0) {
+ if(n&15) PRINTF(L_CC_CAMD,"AES crypted message length not a multiple of 16");
+ Decrypt(data,n);
+ }
+ return n;
+}
+
+bool cCardClientCommon::CanHandle(unsigned short SysId)
+{
+ return (emmProcessing && SysId==CAID) || cCardClient::CanHandle(SysId);
+}
+
+bool cCardClientCommon::Init(const char *config)
+{
+ cMutexLock lock(this);
+ so.Disconnect();
+ int num=0;
+ if(ParseStdConfig(config,&num) &&
+ ParseUserConfig(config,&num) &&
+ (!doAES || ParseKeyConfig(config,&num))) {
+ return (emmAllowed && logReply && Immediate()) ? Login() : true;
+ }
+ return false;
+}
+
+bool cCardClientCommon::Login(void)
+{
+ so.Disconnect();
+ if(!so.Connect(hostname,port)) return false;
+ PRINTF(L_CC_LOGIN,"%s: connected to %s:%d (%s)",name,hostname,port,name);
+ emmProcessing=false;
+
+ unsigned char buff[128];
+ if(conReply) {
+ if(RecvMsg(&so,buff,sizeof(buff))!=16) {
+ PRINTF(L_CC_CAMD,"bad connect reply");
+ return false;
+ }
+ }
+
+ memset(buff,0,32);
+ int user_len=strlen(username)+1;
+ memcpy(buff+1,username,user_len);
+ int pass_len=strlen(password)+1;
+ memcpy(buff+1+user_len+1,password,pass_len);
+ int vers_len=strlen(CCVERSION)+1;
+ memcpy(buff+1+user_len+pass_len+1,CCVERSION,vers_len);
+ PRINTF(L_CC_CAMD,"login user='%s' password=hidden version=%s",username,CCVERSION);
+ if(!SendMsg(&so,buff,32)) return false;
+
+ if(emmAllowed && logReply) {
+ PRINTF(L_CC_CAMD,"waiting for login reply ...");
+ int r=RecvMsg(&so,buff,sizeof(buff));
+ if(r>0) HandleEMMRequest(buff,r);
+ }
+ PRINTF(L_CC_LOGIN,"%s: login done",name);
+ return true;
+}
+
+void cCardClientCommon::HandleEMMRequest(const unsigned char *buff, int len)
+{
+ if(len>=13 && buff[0]==0 && !CheckNull(buff,len) && memcmp(buff,lastEmmReq,13)) {
+ CAID=buff[1]*256+buff[2];
+ ResetIdSet();
+ SetCard(new cCardIrdeto(buff[6],&buff[3]));
+ AddProv(new cProviderIrdeto(0,&buff[7]));
+ AddProv(new cProviderIrdeto(2,&buff[10]));
+ memcpy(lastEmmReq,buff,13);
+ PRINTF(L_CC_LOGIN,"%s: CAID: %04x HexSerial: %02X%02X%02X, HexBase: %02X",name,CAID,buff[3],buff[4],buff[5],buff[6]);
+ PRINTF(L_CC_LOGIN,"%s: Provider00: %02X%02X%02X, Provider10: %02X%02X%02X",name,buff[7],buff[8],buff[9],buff[10],buff[11],buff[12]);
+ if(!emmAllowed) PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+ emmProcessing=true;
+ }
+}
+
+bool cCardClientCommon::ProcessEMM(int caSys, const unsigned char *source)
+{
+ if(emmProcessing && emmAllowed) {
+ cMutexLock lock(this);
+ if(MatchEMM(source)) {
+ const int length=SCT_LEN(source);
+ int id=msEMM.Get(source,length,0);
+ if(id>0) {
+ unsigned char buff[length+32];
+ buff[0]=0x03;
+ buff[1]=(caSys>>8);
+ buff[2]=(caSys&0xFF);
+ memcpy(buff+3,((cCardIrdeto *)card)->hexSer,3);
+ buff[6]=((cCardIrdeto *)card)->hexBase;
+ memcpy(&buff[7],source,length);
+ //PRINTF(L_CC_CAMD,"%s: sending EMM for caid 0x%04X",name,caSys);
+ SendMsg(&so,buff,length+7);
+ msEMM.Cache(id,true,0);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool cCardClientCommon::ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw)
+{
+ Lock();
+ bool res=false;
+ while(exclusive) sleepCond.Wait(*this);
+ if((so.Connected() || Login()) && (!emmProcessing || CanHandle(ecm->caId))) {
+ const int length=SCT_LEN(source);
+ unsigned char buff[length+32];
+ int n;
+ while((n=RecvMsg(&so,buff,16,0))>0) HandleEMMRequest(buff,n);
+ buff[0]=0x02;
+ buff[1]=(ecm->caId>>8);
+ buff[2]=(ecm->caId&0xFF);
+ memset(&buff[3],0,4);
+ memcpy(&buff[7],source,length);
+ if(SendMsg(&so,buff,length+7)) {
+ exclusive=true;
+ time.Set(CCTIMEOUT);
+ do {
+ sleepCond.TimedWait(*this,50);
+ while((n=RecvMsg(&so,buff,32,0))>0) {
+ if(n>=21 && buff[0]==2) {
+ if(!CheckNull(buff+5,16)) {
+ if(!res) {
+ memcpy(cw,buff+5,16);
+ res=true;
+ }
+ else PRINTF(L_CC_CAMD,"unexpected CW packet");
+ }
+ else {
+ PRINTF(L_CC_ECM,"%s: server is unable to handle ECM",name);
+ n=-1;
+ break;
+ }
+ }
+ else HandleEMMRequest(buff,n);
+ }
+ } while(!res && n>=0 && !time.TimedOut());
+ if(!res && time.TimedOut()) PRINTF(L_CC_ECM,"%s: CW request timed out",name);
+ exclusive=false;
+ sleepCond.Broadcast();
+ }
+ }
+ Unlock();
+ return res;
+}
+
+// -- cCardClientCamd33 --------------------------------------------------------
+
+class cCardClientCamd33 : public cCardClientCommon {
+public:
+ cCardClientCamd33(const char *Name);
+ };
+
+static cCardClientLinkReg<cCardClientCamd33> __camd33("Camd33");
+
+cCardClientCamd33::cCardClientCamd33(const char *Name)
+:cCardClientCommon(Name,true,true,true,0)
+{}
+
+// -- cCardClientCardd ---------------------------------------------------------
+
+class cCardClientCardd : public cCardClientCommon {
+public:
+ cCardClientCardd(const char *Name);
+ };
+
+static cCardClientLinkReg<cCardClientCardd> __cardd("Cardd");
+
+cCardClientCardd::cCardClientCardd(const char *Name)
+:cCardClientCommon(Name,false,true,false,96)
+{}
+
+// -- cCardClientBuffy ---------------------------------------------------------
+
+#define MAX_CAIDS 16
+
+class cCardClientBuffy : public cCardClientCommon {
+private:
+ unsigned short CAIDs[MAX_CAIDS], numCAIDs;
+protected:
+ virtual bool Login(void);
+public:
+ cCardClientBuffy(const char *Name);
+ virtual bool Init(const char *config);
+ virtual bool CanHandle(unsigned short SysId);
+ };
+
+static cCardClientLinkReg<cCardClientBuffy> __buffy("Buffy");
+
+cCardClientBuffy::cCardClientBuffy(const char *Name)
+:cCardClientCommon(Name,true,false,true,0)
+{}
+
+bool cCardClientBuffy::Init(const char *config)
+{
+ cMutexLock lock(this);
+ if(cCardClientCommon::Init(config)) {
+ return Immediate() ? Login() : true;
+ }
+ return false;
+}
+
+bool cCardClientBuffy::CanHandle(unsigned short SysId)
+{
+ cMutexLock lock(this);
+ for(int i=0; i<numCAIDs; i++) if(CAIDs[i]==SysId) return true;
+ return false;
+}
+
+bool cCardClientBuffy::Login(void)
+{
+ cMutexLock lock(this);
+ if(!cCardClientCommon::Login()) return false;
+
+ unsigned char buff[128];
+ memset(buff,0,sizeof(buff));
+ buff[0]=0x0A;
+ if(!SendMsg(&so,buff,32)) return false;
+ int n=RecvMsg(&so,buff,sizeof(buff));
+ if(n<0) return false;
+ for(int i=1; i<n && numCAIDs<MAX_CAIDS; i+=2) {
+ unsigned short caid=(buff[i+1]<<8)+buff[i];
+ if(caid==0xFFFF) break;
+ if(caid) CAIDs[numCAIDs++]=caid;
+ }
+ LBSTART(L_CC_LOGIN);
+ LBPUT("%s: CAIDs ",name);
+ for(int i=0; i<numCAIDs && CAIDs[i]; i++) LBPUT("%04X ",CAIDs[i]);
+ LBEND();
+ return true;
+}
+
+// -- cCardClientCamd35 --------------------------------------------------------
+
+struct CmdBlock {
+ unsigned int ucrc;
+ struct {
+ unsigned char cmd; // 0
+ unsigned char len; // 1
+ unsigned char pad[2]; // 2 XXXX
+ unsigned int crc; // 4
+ } udp_header;
+ struct {
+ unsigned short srvID; // 8
+ unsigned short casID; // 10
+ unsigned int prvID; // 12
+ unsigned short pinID; // 16
+ unsigned char pad2[2];// 18 XXXX
+ } service;
+ unsigned char data[2]; // 20
+ };
+
+#define CBSIZE(cb) (sizeof((cb)->udp_header)+sizeof((cb)->service))
+#define UCSIZE(cb) (sizeof((cb)->ucrc))
+#define HDSIZE(cb) (CBSIZE(cb)+UCSIZE(cb))
+
+struct EmmReq02 {
+ unsigned short caid;
+ unsigned char hex[4];
+ unsigned char D0, D2, D3;
+ };
+
+struct EmmReq05 {
+ unsigned short caids[8]; // 0
+ int caidCount; // 16
+ unsigned char ua[6]; // 20
+ unsigned short provMap; // 26
+ struct { // 28
+ unsigned char prov[2];
+ unsigned char sa[3];
+ } provInfo[16];
+ unsigned char D0, D2, D3; // 108
+ };
+
+class cCardClientCamd35 : public cCardClient, public cAES, private cIdSet {
+private:
+ unsigned int ucrc, pinid;
+ bool exclusive, emmCmd06;
+ cCondVar sleepCond;
+ cTimeMs time;
+protected:
+ cNetSocket so;
+ bool emmProcessing;
+ char username[33], password[33];
+ int numCaids, Caids[8];
+ unsigned char Dx[8];
+ int lastEmmReq;
+ //
+ virtual bool Login(void);
+ bool ParseUserConfig(const char *config, int *num);
+ bool SendBlock(struct CmdBlock *cb, int datalen);
+ int RecvBlock(struct CmdBlock *cb, int maxlen, int to);
+ void HandleEMMRequest(const struct CmdBlock *cb);
+ bool CanHandleEMM(unsigned short SysId);
+public:
+ cCardClientCamd35(const char *Name);
+ virtual bool Init(const char *config);
+ virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+ virtual bool ProcessEMM(int caSys, const unsigned char *data);
+ };
+
+static cCardClientLinkReg<cCardClientCamd35> __camd35("Camd35");
+
+cCardClientCamd35::cCardClientCamd35(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,CCTIMEOUT/1000,DEFAULT_IDLE_TIMEOUT,true)
+{
+ emmProcessing=exclusive=emmCmd06=false; pinid=0; numCaids=0; lastEmmReq=0;
+ memset(Dx,0,sizeof(Dx));
+}
+
+bool cCardClientCamd35::ParseUserConfig(const char *config, int *num)
+{
+ int startNum=*num;
+ if(sscanf(&config[*num],":%32[^:]:%32[^:]%n",username,password,num)==2) {
+ *num+=startNum;
+ ucrc=bswap_32(crc32_le(0,MD5((unsigned char *)username,strlen(username),0),16));
+ PRINTF(L_CC_CORE,"%s: username=%s password=%s ucrc=%08x",name,username,password,ucrc);;
+ SetKey(MD5((unsigned char *)password,strlen(password),0));
+ return true;
+ }
+ return false;
+}
+
+bool cCardClientCamd35::SendBlock(struct CmdBlock *cb, int datalen)
+{
+ cb->udp_header.len=datalen;
+ cb->udp_header.crc=bswap_32(crc32_le(0,&cb->data[0],datalen));
+ datalen+=CBSIZE(cb);
+ unsigned char buff[datalen+UCSIZE(cb)+16];
+ *((unsigned int *)buff)=ucrc;
+ HEXDUMP(L_CC_CAMDEXTR,cb,datalen+UCSIZE(cb),"send:");
+ const int l=Encrypt((unsigned char *)cb+UCSIZE(cb),datalen,buff+UCSIZE(cb));
+ if(l<=0) return false;
+ return cCardClient::SendMsg(&so,buff,l+UCSIZE(cb));
+}
+
+int cCardClientCamd35::RecvBlock(struct CmdBlock *cb, int maxlen, int to)
+{
+ int n=cCardClient::RecvMsg(&so,(unsigned char *)cb,maxlen,to);
+ if(n<=0) return n;
+ if((unsigned int)n>=CBSIZE(cb)+UCSIZE(cb)) {
+ if(cb->ucrc==ucrc) {
+ Decrypt((unsigned char *)cb+UCSIZE(cb),n-UCSIZE(cb));
+ if((unsigned int)n<cb->udp_header.len+CBSIZE(cb)+UCSIZE(cb))
+ PRINTF(L_CC_CAMD35,"packet length doesn't match data length");
+ else if(cb->udp_header.crc!=bswap_32(crc32_le(0,&cb->data[0],cb->udp_header.len)))
+ PRINTF(L_CC_CAMD35,"data crc failed");
+ else {
+ HEXDUMP(L_CC_CAMDEXTR,cb,n,"recv:");
+ return n;
+ }
+ }
+ else PRINTF(L_CC_CAMD35,"wrong ucrc: got %08x, want %08x",cb->ucrc,ucrc);
+ }
+ else PRINTF(L_CC_CAMD35,"short packet received");
+ return -1;
+}
+
+bool cCardClientCamd35::Init(const char *config)
+{
+ cMutexLock lock(this);
+ so.Disconnect();
+ int num=0;
+ return (ParseStdConfig(config,&num) && ParseUserConfig(config,&num));
+}
+
+bool cCardClientCamd35::Login(void)
+{
+ so.Disconnect();
+ if(!so.Connect(hostname,port)) return false;
+ PRINTF(L_CC_LOGIN,"%s: connected to %s:%d (%s)",name,hostname,port,name);
+ emmProcessing=false; lastEmmReq=0;
+ PRINTF(L_CC_LOGIN,"%s: login done",name);
+ return true;
+}
+
+void cCardClientCamd35::HandleEMMRequest(const struct CmdBlock *cb)
+{
+ int c=crc32_le(0,cb->data,cb->udp_header.len);
+ if(c!=lastEmmReq) {
+ lastEmmReq=c;
+ ResetIdSet();
+ if(cb->udp_header.cmd==0x02 && cb->udp_header.len>=9) {
+ struct EmmReq02 *req=(struct EmmReq02 *)cb->data;
+ numCaids=1;
+ Caids[0]=bswap_16(req->caid);
+ SetCard(new cCardIrdeto(req->hex[3],&req->hex[0]));
+ Dx[0]=req->D0;
+ Dx[2]=req->D2;
+ Dx[3]=req->D3;
+ char str[20];
+ PRINTF(L_CC_LOGIN,"%s: CAID: %04x HexSerial: %s, HexBase: %02X (D0-%d D2-%d D3-%d)\n",
+ name,Caids[0],HexStr(str,((cCardIrdeto *)card)->hexSer,3),((cCardIrdeto *)card)->hexBase,
+ Dx[0],Dx[2],Dx[3]);
+ if(!emmProcessing || emmCmd06) PRINTF(L_CC_CAMD35,"got cmd 02, doing cmd02 EMM");
+ emmCmd06=false;
+ }
+ else if(cb->udp_header.cmd==0x05 && cb->udp_header.len>=112) {
+ struct EmmReq05 *req=(struct EmmReq05 *)cb->data;
+ numCaids=bswap_32(req->caidCount);
+ for(int i=numCaids-1; i>=0; i--) Caids[i]=bswap_16(req->caids[i]);
+ LBSTARTF(L_CC_LOGIN);
+ LBPUT("%s: CAIDS:",name);
+ for(int i=numCaids-1; i>=0; i--) LBPUT(" %04x",Caids[i]);
+ char str[20], str2[20];
+ LBPUT(" ua=%s",HexStr(str,req->ua,6));
+ switch(Caids[0]>>8) {
+ case 0x17:
+ case 0x06: SetCard(new cCardIrdeto(req->ua[3],&req->ua[0])); break;
+ case 0x01: SetCard(new cCardSeca(&req->ua[0])); break;
+ case 0x0d: SetCard(new cCardCryptoworks(&req->ua[0])); break;
+ default:
+ LBPUT(" (unhandled)");
+ break;
+ }
+ LBPUT(" providers");
+ int map=bswap_16(req->provMap);
+// for(int i=0; i<15; i++) //XXX not sure what actualy is the correct
+// if(map & (1<<i)) { //XXX interpretation of provMap
+ for(int i=0; i<map && i<15; i++) {
+ LBPUT(" %d:%s/%s",i,HexStr(str,req->provInfo[i].prov,2),HexStr(str2,req->provInfo[i].sa,3));
+ if(!CheckNull(req->provInfo[i].sa,3) && !CheckFF(req->provInfo[i].sa,3)) {
+ switch(Caids[0]>>8) {
+ case 0x17:
+ case 0x06: AddProv(new cProviderIrdeto(req->provInfo[i].prov[0],req->provInfo[i].sa)); break;
+ case 0x01: AddProv(new cProviderSeca(req->provInfo[i].prov,req->provInfo[i].sa)); break;
+ default:
+ LBPUT(" <unhandled>");
+ break;
+ }
+ }
+ }
+ Dx[0]=req->D0;
+ Dx[2]=req->D2;
+ Dx[3]=req->D3;
+ LBPUT(" (D0-%d D2-%d D3-%d)",Dx[0],Dx[2],Dx[3]);
+ LBEND();
+
+ if(!emmProcessing || !emmCmd06) PRINTF(L_CC_CAMD35,"got cmd 05, doing cmd06 EMM");
+ emmCmd06=true;
+ }
+ else return;
+
+ if(!emmAllowed) PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+ emmProcessing=true;
+ }
+}
+
+bool cCardClientCamd35::CanHandleEMM(unsigned short SysId)
+{
+ for(int i=numCaids-1; i>=0; i--) if(SysId==Caids[i]) return true;
+ return false;
+}
+
+bool cCardClientCamd35::ProcessEMM(int caSys, const unsigned char *data)
+{
+ if(emmProcessing && emmAllowed) {
+ cMutexLock lock(this);
+ PRINTF(L_CC_CAMDEXTR,"got EMM caSys=%.4x",caSys);
+ if(CanHandleEMM(caSys)) {
+ LDUMP(L_CC_CAMDEXTR,&data[0],10,"EMM starts with");
+ int upd;
+ cProvider *p;
+ cAssembleData ad(data);
+ if(MatchAndAssemble(&ad,&upd,&p)) {
+ PRINTF(L_CC_CAMDEXTR,"EMM matched upd=%d provId=%.4llx",upd,p ? p->ProvId() : 0);
+ while((data=ad.Assembled())) {
+ LDUMP(L_CC_CAMDEXTR,&data[0],10,"processing assembled EMM");
+ if(Dx[upd]) {
+ unsigned char buff[300];
+ memset(buff,0xff,sizeof(buff));
+ struct CmdBlock *cb=(struct CmdBlock *)buff;
+ cb->udp_header.cmd=emmCmd06 ? 0x06 : 0x02;
+ cb->service.srvID=0;
+ cb->service.casID=bswap_16(caSys);
+ cb->service.prvID=bswap_32(p ? p->ProvId() : 0);
+ cb->service.pinID=pinid++;
+ int len=SCT_LEN(data), off=0;
+ switch(caSys>>8) {
+ case 0x0d:
+ {
+ static const unsigned char head[] = { 0x8F,0x70,0x00,0xA4,0x42,0x00,0x00,0x00 };
+ int c, n;
+ switch(data[0]) {
+ case 0x82: c=0x42; n=10; break;
+ case 0x84: c=0x48; n=9; break;
+ case 0x88:
+ case 0x89: c=0x44; n=5; break;
+ default: continue;
+ }
+ if(len<n) continue;
+ off=sizeof(head);
+ memcpy(&cb->data[0],head,off);
+ cb->data[3+1]=c;
+ cb->data[3+4]=len-n;
+ SetSctLen(&cb->data[0],len-n+5);
+ data+=n; len-=n;
+ break;
+ }
+ }
+ if(len+off<=255) {
+ memcpy(&cb->data[off],data,len);
+ int id=msEMM.Get(&cb->data[0],len+off,0);
+ if(id>0) {
+ SendBlock(cb,len+off);
+ msEMM.Cache(id,true,0);
+ }
+ else PRINTF(L_CC_CAMDEXTR,"not send, already in cache");
+ }
+ else PRINTF(L_CC_EMM,"%s: EMM length %d > 255, not supported by UDP protocol",name,len+off);
+ }
+ else PRINTF(L_CC_CAMDEXTR,"dropped, updtype %d blocked",upd);
+ }
+ return true;
+ }
+ else PRINTF(L_CC_CAMDEXTR,"dropped, doesn't match card data");
+ }
+ else PRINTF(L_CC_CAMDEXTR,"dropped, doesn't want caSys %.4x",caSys);
+ }
+ return false;
+}
+
+bool cCardClientCamd35::ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+ bool res=false;
+ const int length=SCT_LEN(data);
+ if(length<=255) {
+ Lock();
+ while(exclusive) sleepCond.Wait(*this);
+ unsigned char buff[300];
+ memset(buff,0xff,sizeof(buff));
+ struct CmdBlock *cb=(struct CmdBlock *)buff;
+ const unsigned int pid=pinid++;
+ int n;
+ while((n=RecvBlock(cb,sizeof(buff),0))>0) {
+ if(cb->udp_header.cmd==0x01 || cb->udp_header.cmd==0x44)
+ PRINTF(L_CC_CAMD35,"unexpected CW answer on flush");
+ else
+ HandleEMMRequest(cb);
+ }
+ cb->udp_header.cmd=0x00;
+ cb->service.srvID=bswap_16(ecm->prgId);
+ cb->service.casID=bswap_16(ecm->caId);
+ switch(ecm->caId>>8) {
+ case 0x18: n=(data[5]*256)+data[6]; break;
+ default: n=ecm->provId; break;
+ }
+ cb->service.prvID=bswap_32(n);
+ cb->service.pinID=pid;
+ memcpy(&cb->data[0],data,length);
+ if(SendBlock(cb,length)) {
+ exclusive=true;
+ time.Set(CCTIMEOUT);
+ do {
+ sleepCond.TimedWait(*this,50);
+ while((n=RecvBlock(cb,sizeof(buff),0))>0) {
+ if(cb->udp_header.cmd==0x01) {
+ if(cb->udp_header.len>=16 && cb->service.pinID==pid && !res) {
+ memcpy(cw,cb->data,16);
+ res=true;
+ }
+ else PRINTF(L_CC_CAMD35,"unexpected/bad CW packet");
+ }
+ else if(cb->udp_header.cmd==0x44) {
+ if(cb->service.pinID==pid) {
+ PRINTF(L_CC_ECM,"%s: server is unable to handle ECM",name);
+ n=-1;
+ break;
+ }
+ }
+ else HandleEMMRequest(cb);
+ }
+ } while(!res && n>=0 && !time.TimedOut());
+ if(!res && time.TimedOut()) PRINTF(L_CC_ECM,"%s: CW request timed out",name);
+ exclusive=false;
+ sleepCond.Broadcast();
+ }
+ Unlock();
+ }
+ else PRINTF(L_CC_ECM,"%s: ECM length %d > 255, not supported by UDP protocol",name,length);
+ return res;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vdr/thread.h>
+#include <vdr/tools.h>
+
+#include "system.h"
+#include "cc.h"
+#include "network.h"
+#include "misc.h"
+#include "opts.h"
+#include "log-core.h"
+
+#define SYSTEM_NAME "Cardclient"
+#define SYSTEM_PRI -15
+
+#define CONF_FILE "cardclient.conf"
+
+static const struct LogModule lm_cc = {
+ (LMOD_ENABLE|L_CC_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_CC_CORE|L_CC_LOGIN|L_CC_ECM|L_CC_EMM|L_CC_CAMD|L_CC_CAMD35|
+ L_CC_RDGD|L_CC_NEWCAMD|L_CC_GBOX)&LOPT_MASK,
+ "cardclient",
+ { "core","login","ecm","emm","camd","camd35","camd35extra","radegast",
+ "newcamd","gbox" }
+ };
+ADD_MODULE(L_CC,lm_cc)
+
+static int immediate=true;
+
+// -- cCardClient --------------------------------------------------------------
+
+cCardClient::cCardClient(const char *Name)
+:msECM(32,16)
+,msEMM(32,0)
+{
+ name=Name;
+ emmAllowed=false; emmCaid[0]=0x1700; emmMask[0]=0xFF00; numCaid=1;
+}
+
+bool cCardClient::Immediate(void)
+{
+ return immediate;
+}
+
+bool cCardClient::ParseStdConfig(const char *config, int *num)
+{
+ int n, emm=0;
+ if(!num) num=&n;
+ if(sscanf(config,"%63[^:]:%d:%d%n",hostname,&port,&emm,num)<3) return false;
+ if(emm>0) emmAllowed=true;
+ if(config[*num]=='/') {
+ numCaid=0;
+ do {
+ emmMask[numCaid]=0xFFFF;
+ int start=(*num)+1;
+ if(sscanf(&config[start],"%x%n/%x%n",&emmCaid[numCaid],num,&emmMask[numCaid],num)<1)
+ return false;
+ *num+=start;
+ numCaid++;
+ } while(numCaid<MAX_CC_CAID && config[*num]==',');
+ }
+ LBSTART(L_CC_CORE);
+ LBPUT("hostname=%s port=%d emm=%d emmCaids",hostname,port,emmAllowed);
+ for(int i=0; i<numCaid; i++) LBPUT(" %04x/%04x",emmCaid[i],emmMask[i]);
+ LBEND();
+ return true;
+}
+
+bool cCardClient::CanHandle(unsigned short SysId)
+{
+ for(int i=0; i<numCaid; i++)
+ if((SysId&emmMask[i])==emmCaid[i]) return true;
+ return false;
+}
+
+bool cCardClient::SendMsg(cNetSocket *so, const unsigned char *data, int len)
+{
+ if(!so->Connected() && !Login()) return false;
+ if(so->Write(data,len)<0) {
+ PRINTF(L_CC_CORE,"send error. reconnecting...");;
+ so->Disconnect();
+ return false;
+ }
+ return true;
+}
+
+int cCardClient::RecvMsg(cNetSocket *so, unsigned char *data, int len, int to)
+{
+ if(!so->Connected() && !Login()) return -1;
+ int n=so->Read(data,len,to);
+ if(n<0 || (n==0 && to!=0)) {
+ PRINTF(L_CC_CORE,"recv error. reconnecting...");;
+ so->Disconnect();
+ return -1;
+ }
+ return n;
+}
+
+// -- cCardClients -------------------------------------------------------------
+
+class cCardClients {
+friend class cCardClientLink;
+private:
+ static cCardClientLink *first;
+ //
+ static void Register(cCardClientLink *ccl);
+public:
+ cCardClientLink *FindByName(const char *name);
+ };
+
+cCardClientLink *cCardClients::first=0;
+
+static cCardClients cardclients;
+
+void cCardClients::Register(cCardClientLink *ccl)
+{
+ PRINTF(L_CORE_DYN,"cardclients: registering cardclient %s",ccl->name);
+ ccl->next=first;
+ first=ccl;
+}
+
+cCardClientLink *cCardClients::FindByName(const char *name)
+{
+ cCardClientLink *ccl=first;
+ while(ccl) {
+ if(!strcasecmp(ccl->name,name)) break;
+ ccl=ccl->next;
+ }
+ return ccl;
+}
+
+// -- cCardClientLink ----------------------------------------------------------
+
+cCardClientLink::cCardClientLink(const char *Name)
+{
+ name=Name;
+ cCardClients::Register(this);
+}
+
+// -- cSystemLinkCardClient -----------------------------------------------------------
+
+class cSystemLinkCardClient : public cSystemLink, private cConfRead, cSimpleList<cCardClient> {
+protected:
+ virtual bool ParseLine(const char *line, bool fromCache);
+public:
+ cSystemLinkCardClient(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void);
+ virtual bool Init(const char *cfgdir);
+ virtual void Clean(void);
+ cCardClient *FindBySysId(unsigned short id, cCardClient *cc);
+ };
+
+static cSystemLinkCardClient staticCcl;
+
+// -- cSystemCardClient ---------------------------------------------------------------
+
+class cSystemCardClient : public cSystem {
+private:
+ cCardClient *cc;
+public:
+ cSystemCardClient(void);
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+ virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+ };
+
+cSystemCardClient::cSystemCardClient(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+ cc=0;
+ local=false; hasLogger=true;
+}
+
+bool cSystemCardClient::ProcessECM(const cEcmInfo *ecm, unsigned char *data)
+{
+ cCardClient *startCc=cc;
+ do {
+ if(cc) {
+ cTimeMs start;
+ int id=cc->msECM.Get(data,SCT_LEN(data),cw);
+ if(id==0 || (id>0 && cc->ProcessECM(ecm,data,cw))) {
+ int dur=start.Elapsed();
+ if(dur>2000) {
+ char bb[32];
+ time_t now=time(0);
+ ctime_r(&now,bb); stripspace(bb);
+ PRINTF(L_CC_CORE,"%s: lagged cw %d ms (%s)",bb,dur,cc->Name());
+ }
+ char buff[32];
+ snprintf(buff,sizeof(buff),"CC %s",cc->Name());
+ KeyOK(buff);
+ if(id>0) cc->msECM.Cache(id,true,cw);
+ return true;
+ }
+ if(id>0) {
+ PRINTF(L_CC_CORE,"client %s (%s:%d) ECM failed (%d ms)",cc->Name(),cc->hostname,cc->port,(int)start.Elapsed());
+ cc->msECM.Cache(id,false,cw);
+ }
+ }
+ if(!cc) PRINTF(L_CC_CORE,"cc-loop");
+ cc=staticCcl.FindBySysId(ecm->caId,cc);
+ if(cc && cc!=startCc) PRINTF(L_CC_CORE,"now trying client %s (%s:%d)",cc->Name(),cc->hostname,cc->port);
+ } while(cc!=startCc);
+ return false;
+}
+
+void cSystemCardClient::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+ cCardClient *cc=0;
+ while((cc=staticCcl.FindBySysId(caid,cc)))
+ cc->ProcessEMM(caid,buffer);
+}
+
+// -- cSystemLinkCardClient -----------------------------------------------------------
+
+static const tI18nPhrase Phrases[] = {
+ { "Cardclient: connect immediately",
+ "Cardclient: sofort verbinden",
+ "",
+ "",
+ "Cardclient: direct contact maken",
+ "",
+ "Cardclient: connecter immediatement",
+ "",
+ "Korttiasiakas: yhdistä välittömästi",
+ "Klient karty: pod³±cz natychmiast",
+ "",
+ "",
+ "",
+ },
+ { NULL }
+ };
+
+cSystemLinkCardClient::cSystemLinkCardClient(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ opts=new cOpts(SYSTEM_NAME,1);
+ opts->Add(new cOptBool("Immediate","Cardclient: connect immediately",&immediate));
+ Feature.AddPhrases(Phrases);
+}
+
+cCardClient *cSystemLinkCardClient::FindBySysId(unsigned short id, cCardClient *cc)
+{
+ if(cc) cc=Next(cc); else cc=First();
+ while(cc) {
+ if(cc->CanHandle(id)) return cc;
+ cc=Next(cc);
+ }
+ return 0;
+}
+
+bool cSystemLinkCardClient::CanHandle(unsigned short SysId)
+{
+ return FindBySysId(SysId,0)!=0;
+}
+
+cSystem *cSystemLinkCardClient::Create(void)
+{
+ return new cSystemCardClient();
+}
+
+bool cSystemLinkCardClient::Init(const char *cfgdir)
+{
+ Clear();
+ ConfRead("cardclient config",AddDirectory(cfgdir,CONF_FILE));
+ PRINTF(L_CC_CORE,"created %d client(s)",Count());
+ return true;
+}
+
+bool cSystemLinkCardClient::ParseLine(const char *line, bool fromCache)
+{
+ char name[32];
+ int num;
+ if(sscanf(line,"%31[^:]:%n",name,&num)==1) {
+ cCardClientLink *ccl=cardclients.FindByName(name);
+ if(ccl) {
+ cCardClient *cc=ccl->Create();
+ if(cc) {
+ if(cc->Init(&line[num])) {
+ Add(cc);
+ PRINTF(L_CC_CORE,"client '%s' ready",cc->Name());
+ return true;
+ }
+ else {
+ delete cc;
+ PRINTF(L_GEN_ERROR,"init of cardclient '%s' failed",name);
+ }
+ }
+ else PRINTF(L_GEN_ERROR,"failed to create cardclient '%s'",name);
+ }
+ else PRINTF(L_GEN_ERROR,"no client found for card server type '%s'",name);
+ }
+ return false;
+}
+
+void cSystemLinkCardClient::Clean(void)
+{
+ Clear();
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___SYSTEM_CC_H
+#define ___SYSTEM_CC_H
+
+#include <vdr/thread.h>
+#include "system.h"
+#include "misc.h"
+#include "log.h"
+
+#define L_CC 6
+#define L_CC_CORE LCLASS(L_CC,0x2)
+#define L_CC_LOGIN LCLASS(L_CC,0x4)
+#define L_CC_ECM LCLASS(L_CC,0x8)
+#define L_CC_EMM LCLASS(L_CC,0x10)
+#define L_CC_CAMD LCLASS(L_CC,0x20)
+#define L_CC_CAMD35 LCLASS(L_CC,0x40)
+#define L_CC_CAMDEXTR LCLASS(L_CC,0x80)
+#define L_CC_RDGD LCLASS(L_CC,0x100)
+#define L_CC_NEWCAMD LCLASS(L_CC,0x200)
+#define L_CC_GBOX LCLASS(L_CC,0x400)
+
+#define L_CC_ALL LALL(L_CC_GBOX)
+
+// ----------------------------------------------------------------
+
+class cEcmInfo;
+class cNetSocket;
+class cCardClients;
+class cSystemCardClient;
+
+// ----------------------------------------------------------------
+
+#define MAX_CC_CAID 16
+
+class cCardClient : public cSimpleItem, protected cMutex {
+friend class cSystemCardClient;
+protected:
+ const char *name;
+ char hostname[64];
+ int port;
+ bool emmAllowed;
+ int emmCaid[MAX_CC_CAID], emmMask[MAX_CC_CAID], numCaid;
+ cMsgCache msECM, msEMM;
+ //
+ bool ParseStdConfig(const char *config, int *num=0);
+ virtual bool SendMsg(cNetSocket *so, const unsigned char *data, int len);
+ virtual int RecvMsg(cNetSocket *so, unsigned char *data, int len, int to=-1);
+ virtual bool Login(void) { return false; }
+ bool Immediate(void);
+public:
+ cCardClient(const char *Name);
+ virtual bool Init(const char *config)=0;
+ virtual bool CanHandle(unsigned short SysId);
+ virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)=0;
+ virtual bool ProcessEMM(int caSys, const unsigned char *data) { return false; }
+ const char *Name(void) { return name; }
+ };
+
+// ----------------------------------------------------------------
+
+class cCardClientLink {
+friend class cCardClients;
+private:
+ cCardClientLink *next;
+protected:
+ const char *name;
+public:
+ cCardClientLink(const char *Name);
+ virtual ~cCardClientLink() {};
+ virtual cCardClient *Create(void)=0;
+ };
+
+// ----------------------------------------------------------------
+
+template<class CC> class cCardClientLinkReg : public cCardClientLink {
+public:
+ cCardClientLinkReg(const char *Name):cCardClientLink(Name) {}
+ virtual cCardClient *Create(void) { return new CC(name); }
+ };
+
+#endif //___SYSTEM_CC_H
--- /dev/null
+#
+# Cardclient
+#
+TARGET = cardclient
+OBJS = cc.o camd.o radegast.o aroureos.o newcamd.o gbox.o
+LIBS = -lcrypto -lcrypt
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ *
+ * This code is based on morfsta's version but has been rewritten nearly
+ * from scratch.
+ *
+ * To get it working ensure that GBOX is running with no F: { 01 } line,
+ * and no pmt.tmp file in /var/tmp.
+ */
+
+#include <stdio.h>
+
+#include <vdr/pat.h>
+
+#include "cc.h"
+#include "network.h"
+#include "parse.h"
+
+// -- cGboxClient -----------------------------------------------------------
+
+class cCardClientGbox : public cCardClient {
+private:
+ cNetSocket so;
+ //
+ int GetMsg(int cmd, unsigned char *buff, int len);
+protected:
+ virtual bool Login(void);
+public:
+ cCardClientGbox(const char *Name);
+ virtual bool Init(const char *CfgDir);
+ virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *Cw);
+ };
+
+static cCardClientLinkReg<cCardClientGbox> __ncd("gbox");
+
+cCardClientGbox::cCardClientGbox(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,2,DEFAULT_IDLE_TIMEOUT,true)
+{}
+
+bool cCardClientGbox::Init(const char *config)
+{
+ cMutexLock lock(this);
+ int num=0;
+ if(!ParseStdConfig(config,&num)) return false;
+ return Immediate() ? Login() : true;
+}
+
+bool cCardClientGbox::Login(void)
+{
+ so.Disconnect();
+ if(!so.Bind("127.0.0.1",8003)) return false;
+ return true;
+}
+
+int cCardClientGbox::GetMsg(int cmd, unsigned char *buff, int len)
+{
+ int n;
+ do {
+ n=so.Read(buff,len);
+ if(n<=0) {
+ if(n==0) PRINTF(L_CC_GBOX,"timeout on GetMsg.");
+ break;
+ }
+ } while(buff[0]!=cmd);
+ return n;
+}
+
+bool cCardClientGbox::ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+ cMutexLock lock(this);
+ if((!so.Connected() && !Login()) || !CanHandle(ecm->caId)) return false;
+ so.Flush();
+
+ const int caid=ecm->caId;
+ const int pid =ecm->ecm_pid;
+
+ static const unsigned char pmt[] = {
+ 0x87,
+ 0x02,0xb0,0x25, // tid, sct len
+ 0x17,0x74, // sid
+ 0xc3,0x00,0x00, // vers, sctnr
+ 0xff,0xec, // pcr pid
+ 0xf0,0x00, // prg info len
+ 0x02, 0xff,0xec, 0xf0,0x00
+ };
+ unsigned char buff[512];
+ memcpy(buff,pmt,sizeof(pmt));
+ buff[4]=ecm->prgId >> 8;
+ buff[5]=ecm->prgId & 0xFF;
+#if APIVERSNUM >= 10500
+ int casys[2];
+#else
+ unsigned short casys[2];
+#endif
+ casys[0]=caid;
+ casys[1]=0;
+ bool streamFlag;
+ int n=GetCaDescriptors(ecm->source,ecm->transponder,ecm->prgId,casys,sizeof(buff)-sizeof(pmt),&buff[sizeof(pmt)],streamFlag);
+ if(n<=0) {
+ PRINTF(L_CC_GBOX,"no CA descriptor for caid %04x sid %d prov %04x",caid,ecm->prgId,ecm->provId);
+ return false;
+ }
+ buff[16]=0xF0 | ((n>>8)&0x0F);
+ buff[17]=n & 0xFF;
+ SetSctLen(&buff[1],sizeof(pmt)-4+n+4);
+ buff[2]=(buff[2]&0x0F)|0xB0;
+
+ if(so.SendTo("127.0.0.1",8004,buff,sizeof(pmt)+n+4)<(int)sizeof(pmt)) {
+ PRINTF(L_CC_GBOX,"failed to send PMT data. GBOX running?");
+ return false;
+ }
+
+ if((n=GetMsg(0x8a,buff,sizeof(buff)))<=0) {
+ PRINTF(L_CC_GBOX,"failed to get ECM port. GBOX running?");
+ return false;
+ }
+ int pidnum=-1;
+ if(n>=2) {
+ for(int i=0 ; i<buff[1]; i++) {
+ if(WORD(buff,2+i*2,0x1FFF)==pid) {
+ pidnum=i;
+ break;
+ }
+ }
+ }
+ if(pidnum<0) {
+ PRINTF(L_CC_ECM,"%s: unable to decode for CAID %04X/PID %04X",name,caid,pid);
+ return false;
+ }
+
+ n=SCT_LEN(data);
+ if(n>=256) {
+ PRINTF(L_CC_ECM,"%s: ECM section too long %d > 255",name,n);
+ return false;
+ }
+ buff[0]=0x88;
+ buff[1]=(pid>>8)&0x1F;
+ buff[2]=pid & 0xFF;
+ buff[3]=n;
+ memcpy(&buff[4],data,n);
+ n+=4;
+ if(so.SendTo("127.0.0.1",8005+pidnum,buff,n)<n) {
+ PRINTF(L_CC_GBOX,"failed to send ECM data. GBOX running?");
+ return false;
+ }
+
+ if(GetMsg(0x89,buff,sizeof(buff))<=0) {
+ PRINTF(L_CC_GBOX,"failed to get CW. GBOX running?");
+ return false;
+ }
+ if(n<17) {
+ PRINTF(L_CC_GBOX,"bad CW answer from GBOX");
+ return false;
+ }
+ memcpy(cw,&buff[1],16);
+ return true;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <crypt.h>
+#include <byteswap.h>
+
+#include <vdr/thread.h>
+
+#include "cc.h"
+#include "network.h"
+#include "misc.h"
+#include "parse.h"
+
+#include <openssl/des.h>
+
+#define CWS_NETMSGSIZE 240
+
+// -- cTripleDes ---------------------------------------------------------------
+
+class cTripleDes {
+private:
+ DES_key_schedule ks1,ks2;
+ //
+ void SetOddParity(unsigned char *key); // key must be 16 bytes!
+protected:
+ unsigned char desKey[16];
+ //
+ void ScheduleKey(void);
+ int PadMessage(unsigned char *data, int len);
+ void Expand(unsigned char *expanded, const unsigned char *normal); // 14 byte key input, 16 byte expanded output
+ void Decrypt(unsigned char *data, int len);
+ const unsigned char *Encrypt(const unsigned char *data, int len, unsigned char *crypt);
+ };
+
+void cTripleDes::SetOddParity(unsigned char *key)
+{
+ DES_set_odd_parity((DES_cblock *)&key[0]); // set odd parity on both keys
+ DES_set_odd_parity((DES_cblock *)&key[8]); //
+}
+
+void cTripleDes::ScheduleKey(void)
+{
+ DES_key_sched((DES_cblock *)&desKey[0],&ks1);
+ DES_key_sched((DES_cblock *)&desKey[8],&ks2);
+}
+
+void cTripleDes::Expand(unsigned char *expand, const unsigned char *normal)
+{
+ expand[0] = normal[0] & 0xfe;
+ expand[1] = ((normal[0] << 7) | (normal[1] >> 1)) & 0xfe;
+ expand[2] = ((normal[1] << 6) | (normal[2] >> 2)) & 0xfe;
+ expand[3] = ((normal[2] << 5) | (normal[3] >> 3)) & 0xfe;
+ expand[4] = ((normal[3] << 4) | (normal[4] >> 4)) & 0xfe;
+ expand[5] = ((normal[4] << 3) | (normal[5] >> 5)) & 0xfe;
+ expand[6] = ((normal[5] << 2) | (normal[6] >> 6)) & 0xfe;
+ expand[7] = normal[6] << 1;
+ expand[8] = normal[7] & 0xfe;
+ expand[9] = ((normal[7] << 7) | (normal[8] >> 1)) & 0xfe;
+ expand[10] = ((normal[8] << 6) | (normal[9] >> 2)) & 0xfe;
+ expand[11] = ((normal[9] << 5) | (normal[10] >> 3)) & 0xfe;
+ expand[12] = ((normal[10] << 4) | (normal[11] >> 4)) & 0xfe;
+ expand[13] = ((normal[11] << 3) | (normal[12] >> 5)) & 0xfe;
+ expand[14] = ((normal[12] << 2) | (normal[13] >> 6)) & 0xfe;
+ expand[15] = normal[13] << 1;
+ SetOddParity(expand);
+}
+
+int cTripleDes::PadMessage(unsigned char *data, int len)
+{
+ DES_cblock padBytes;
+ unsigned char noPadBytes;
+
+ noPadBytes = (8 - ((len - 1) % 8)) % 8;
+ if(len+noPadBytes+1 >= CWS_NETMSGSIZE-8) {
+ PRINTF(L_CC_NEWCAMD,"message overflow in cTripleDes::PadMessage");
+ return -1;
+ }
+
+ srand(time(NULL)); // make sure the random generator is initialized
+ DES_random_key((DES_cblock *)padBytes);
+ memcpy(data+len,padBytes,noPadBytes); len+=noPadBytes;
+ data[len]=XorSum(data+2,len-2);
+ return len+1;
+}
+
+const unsigned char *cTripleDes::Encrypt(const unsigned char *data, int len, unsigned char *crypt)
+{
+ DES_cblock ivec;
+ DES_random_key((DES_cblock *)ivec);
+ memcpy(crypt+len,ivec,sizeof(ivec));
+ DES_ede2_cbc_encrypt(data+2,crypt+2,len-2,&ks1,&ks2,(DES_cblock *)ivec,DES_ENCRYPT);
+ return crypt;
+}
+
+void cTripleDes::Decrypt(unsigned char *data, int len)
+{
+ if((len-2) % 8 || (len-2)<16) {
+ PRINTF(L_CC_NEWCAMD,"warning: encrypted data size mismatch");
+ return;
+ }
+ DES_cblock ivec;
+ len-=sizeof(ivec); memcpy(ivec, data+len, sizeof(ivec));
+ DES_ede2_cbc_encrypt(data+2,data+2,len-2,&ks1,&ks2,(DES_cblock *)ivec,DES_DECRYPT);
+}
+
+// -- cNewCamdClient -----------------------------------------------------------
+
+#define USERLEN 32
+#define PASSWDLEN 32
+#define CWS_FIRSTCMDNO 0xe0
+
+typedef enum {
+ MSG_CLIENT_2_SERVER_LOGIN = CWS_FIRSTCMDNO,
+ MSG_CLIENT_2_SERVER_LOGIN_ACK,
+ MSG_CLIENT_2_SERVER_LOGIN_NAK,
+ MSG_CARD_DATA_REQ,
+ MSG_CARD_DATA,
+ MSG_SERVER_2_CLIENT_NAME,
+ MSG_SERVER_2_CLIENT_NAME_ACK,
+ MSG_SERVER_2_CLIENT_NAME_NAK,
+ MSG_SERVER_2_CLIENT_LOGIN,
+ MSG_SERVER_2_CLIENT_LOGIN_ACK,
+ MSG_SERVER_2_CLIENT_LOGIN_NAK,
+ MSG_ADMIN,
+ MSG_ADMIN_ACK,
+ MSG_ADMIN_LOGIN,
+ MSG_ADMIN_LOGIN_ACK,
+ MSG_ADMIN_LOGIN_NAK,
+ MSG_ADMIN_COMMAND,
+ MSG_ADMIN_COMMAND_ACK,
+ MSG_ADMIN_COMMAND_NAK
+ } net_msg_type_t;
+
+typedef enum {
+ COMMTYPE_CLIENT,
+ COMMTYPE_SERVER
+ } comm_type_t;
+
+struct CustomData {
+ union {
+ struct {
+ unsigned short prgId; // Big-Endian
+ unsigned char data[6];
+ } V525;
+ struct {
+ unsigned int prgId; // Big-Endian
+ } V520;
+ };
+ };
+
+class cCardClientNewCamd : public cCardClient, private cTripleDes, private cIdSet {
+private:
+ cNetSocket so;
+ unsigned char configKey[14];
+ unsigned short netMsgId;
+ int caId, protoVers, cdLen;
+ bool emmProcessing;
+ char username[USERLEN], password[PASSWDLEN];
+ //
+ void InitVars(void);
+ void InitProtoVers(int vers);
+ bool NextProto(void);
+ void InitCustomData(struct CustomData *cd, const unsigned short PrgId, const unsigned char *data);
+ void PrepareLoginKey(unsigned char *deskey, const unsigned char *rkey, const unsigned char *ckey);
+protected:
+ virtual bool Login(void);
+public:
+ cCardClientNewCamd(const char *Name);
+ // Client Helper functions
+ bool SendMessage(cNetSocket *so, const unsigned char *data, int len, bool UseMsgId, const struct CustomData *cd=0, comm_type_t commType=COMMTYPE_CLIENT);
+ int ReceiveMessage(cNetSocket *so, unsigned char *data, bool UseMsgId, struct CustomData *cd=0, comm_type_t commType=COMMTYPE_CLIENT);
+ bool CmdSend(cNetSocket *so, net_msg_type_t cmd, comm_type_t commType=COMMTYPE_CLIENT);
+ int CmdReceive(cNetSocket *so, comm_type_t commType=COMMTYPE_CLIENT);
+ //
+ virtual bool Init(const char *CfgDir);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *Cw);
+ virtual bool ProcessEMM(int caSys, const unsigned char *data);
+ };
+
+static cCardClientLinkReg<cCardClientNewCamd> __ncd("Newcamd");
+
+cCardClientNewCamd::cCardClientNewCamd(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,20,DEFAULT_IDLE_TIMEOUT)
+{
+ memset(username,0,sizeof(username));
+ memset(password,0,sizeof(password));
+ InitVars();
+ InitProtoVers(525);
+}
+
+void cCardClientNewCamd::InitVars(void)
+{
+ netMsgId=0; caId=-1; emmProcessing=false;
+ ResetIdSet();
+}
+
+void cCardClientNewCamd::InitProtoVers(int vers)
+{
+ switch(vers) {
+ case 525: protoVers=525; cdLen=8; break;
+ default: protoVers=520; cdLen=4; break;
+ }
+ PRINTF(L_CC_NEWCAMD,"now using protocol version %d (cdLen=%d)",protoVers,cdLen);
+}
+
+bool cCardClientNewCamd::NextProto(void)
+{
+ switch(protoVers) {
+ case 525: InitProtoVers(520); break;
+ default: return false;
+ }
+ return true;
+}
+
+void cCardClientNewCamd::InitCustomData(struct CustomData *cd, const unsigned short PrgId, const unsigned char *data)
+{
+ if(cd) {
+ switch(protoVers) {
+ case 525:
+ cd->V525.prgId=bswap_16(PrgId);
+ if(data) memcpy(cd->V525.data,data,sizeof(cd->V525.data));
+ else memset(cd->V525.data,0,sizeof(cd->V525.data));
+ break;
+ default:
+ cd->V520.prgId=bswap_32((unsigned int)PrgId);
+ break;
+ }
+ }
+}
+
+void cCardClientNewCamd::PrepareLoginKey(unsigned char *deskey, const unsigned char *rkey, const unsigned char *ckey)
+{
+ unsigned char tmpkey[14];
+ for (int i=0; i<(int)sizeof(tmpkey); i++) { tmpkey[i]=rkey[i]^ckey[i]; }
+ Expand(deskey, tmpkey);
+}
+
+bool cCardClientNewCamd::SendMessage(cNetSocket *so, const unsigned char *data, int len, bool UseMsgId, const struct CustomData *cd, comm_type_t commType)
+{
+ if(len<3||len+cdLen+4>CWS_NETMSGSIZE) {
+ PRINTF(L_CC_NEWCAMD,"bad message size %d in SendMessage",len);
+ return false;
+ }
+ unsigned char netbuf[CWS_NETMSGSIZE];
+ memset(&netbuf[2],0,cdLen+2);
+ memcpy(&netbuf[cdLen+4],data,len);
+ netbuf[cdLen+4+1]=(data[1]&0xf0)|(((len-3)>>8)&0x0f);
+ netbuf[cdLen+4+2]=(len-3)&0xff;
+ len+=4;
+ if(cd) memcpy(&netbuf[4],cd,cdLen);
+ len+=cdLen;
+ if(UseMsgId) {
+ if(commType==COMMTYPE_CLIENT) netMsgId++;
+ netbuf[2]=netMsgId>>8;
+ netbuf[3]=netMsgId&0xff;
+ }
+ if((len=cTripleDes::PadMessage(netbuf,len))<0) {
+ PRINTF(L_CC_NEWCAMD,"PadMessage failed");
+ return false;
+ }
+ if((data=cTripleDes::Encrypt(netbuf,len,netbuf))==0) {
+ PRINTF(L_CC_NEWCAMD,"Encrypt failed");
+ return false;
+ }
+ len+=sizeof(DES_cblock);
+ netbuf[0]=(len-2)>>8;
+ netbuf[1]=(len-2)&0xff;
+ return cCardClient::SendMsg(so,netbuf,len);
+}
+
+int cCardClientNewCamd::ReceiveMessage(cNetSocket *so, unsigned char *data, bool UseMsgId, struct CustomData *cd, comm_type_t commType)
+{
+ unsigned char netbuf[CWS_NETMSGSIZE];
+ int len=cCardClient::RecvMsg(so,netbuf,2);
+ if(len!=2) {
+ if(len>0) PRINTF(L_CC_NEWCAMD,"bad length %d != 2 on message length read",len);
+ return 0;
+ }
+ const int mlen=WORD(netbuf,0,0xFFFF);
+ if(mlen>CWS_NETMSGSIZE-2) {
+ PRINTF(L_CC_NEWCAMD,"receive message buffer overflow");
+ return 0;
+ }
+ len=cCardClient::RecvMsg(so,netbuf+2,mlen);
+ if(len!=mlen) {
+ PRINTF(L_CC_NEWCAMD,"bad length %d != %d on message read",len,mlen);
+ return 0;
+ }
+ len+=2;
+ cTripleDes::Decrypt(netbuf,len); len-=sizeof(DES_cblock);
+ if(XorSum(netbuf+2, len-2)) {
+ PRINTF(L_CC_NEWCAMD,"checksum error");
+ return 0;
+ }
+
+ int returnLen=WORD(netbuf,5+cdLen,0x0FFF)+3;
+ if(cd) memcpy(cd,&netbuf[4],cdLen);
+ if(UseMsgId) {
+ switch(commType) {
+ case COMMTYPE_SERVER:
+ netMsgId=WORD(netbuf,2,0xFFFF);
+ break;
+ case COMMTYPE_CLIENT:
+ if(netMsgId!=WORD(netbuf,2,0xFFFF)) {
+ PRINTF(L_CC_NEWCAMD,"bad msgid %04x != %04x ",netMsgId,WORD(netbuf,2,0xFFFF));
+ return -1;
+ }
+ break;
+ default:
+ PRINTF(L_CC_NEWCAMD,"unknown commType %x",commType);
+ return -1;
+ }
+ }
+ memcpy(data,netbuf+4+cdLen,returnLen);
+ return returnLen;
+}
+
+bool cCardClientNewCamd::CmdSend(cNetSocket *so, net_msg_type_t cmd, comm_type_t commType)
+{
+ unsigned char buffer[3];
+ buffer[0] = cmd; buffer[1] = buffer[2] = 0;
+ return SendMessage(so,buffer,sizeof(buffer),false,0,commType);
+}
+
+int cCardClientNewCamd::CmdReceive(cNetSocket *so, comm_type_t commType)
+{
+ unsigned char buffer[CWS_NETMSGSIZE];
+ if(ReceiveMessage(so,buffer,false,0,commType)!=3) return -1;
+ return buffer[0];
+}
+
+bool cCardClientNewCamd::CanHandle(unsigned short SysId)
+{
+ return (caId>=0 && SysId==caId) || cCardClient::CanHandle(SysId);
+}
+
+bool cCardClientNewCamd::Init(const char *config)
+{
+ cMutexLock lock(this);
+ int num=0;
+ char key[29];
+ const char *tmp=key;
+ if(!ParseStdConfig(config,&num)
+ || sscanf(&config[num],":%31[^:]:%31[^:]:%28[^:]",username,password,key)!=3
+ || GetHex(tmp,configKey,sizeof(configKey),false)!=14) return false;
+ char str[32];
+ PRINTF(L_CC_CORE,"%s: username=%s password=%s key=%s",name,username,password,HexStr(str,configKey,14));
+ return Immediate() ? Login() : true;
+}
+
+bool cCardClientNewCamd::Login(void)
+{
+ so.Disconnect();
+ if(!so.Connect(hostname,port)) return false;
+
+ InitVars();
+ unsigned char randData[14];
+ if(so.Read(randData,sizeof(randData))!=14) {
+ PRINTF(L_CC_NEWCAMD,"no connect answer from %s:%d",hostname,port);
+ so.Disconnect();
+ return false;
+ }
+
+ char *crPasswd=crypt(password,"$1$abcdefgh$");
+ unsigned char buffer[CWS_NETMSGSIZE];
+ const int userLen=strlen(username)+1;
+ const int passLen=strlen(crPasswd)+1;
+
+ // prepare the initial login message
+ buffer[0] = MSG_CLIENT_2_SERVER_LOGIN;
+ buffer[1] = 0;
+ buffer[2] = userLen+passLen;
+ memcpy(&buffer[3],username,userLen);
+ memcpy(&buffer[3]+userLen,crPasswd,passLen);
+
+ // XOR configKey with randData and expand the 14 byte result -> 16 byte
+ PrepareLoginKey(desKey,randData,configKey);
+ cTripleDes::ScheduleKey();
+
+ // set NewCS client identification
+ struct CustomData cd;
+ InitCustomData(&cd,0x5644,0);
+
+ if(!SendMessage(&so,buffer,buffer[2]+3,true,&cd) || CmdReceive(&so)!=MSG_CLIENT_2_SERVER_LOGIN_ACK) {
+ PRINTF(L_CC_NEWCAMD,"failed to login to cardserver for username %s (proto %d)",username,protoVers);
+ if(NextProto()) return Login();
+ return false;
+ }
+
+ // create the session key (for usage later)
+ unsigned char tmpkey[14];
+ memcpy(tmpkey, configKey, sizeof(tmpkey));
+ const int passStrLen=strlen(crPasswd);
+ for(int i=0; i<passStrLen; ++i) tmpkey[i%14]^=crPasswd[i];
+
+ cTripleDes::Expand(desKey,tmpkey); // expand 14 byte key -> 16 byte
+ cTripleDes::ScheduleKey();
+
+ if(!CmdSend(&so,MSG_CARD_DATA_REQ) || ReceiveMessage(&so,buffer,false)<=0) return false;
+ if(buffer[0] == MSG_CARD_DATA) {
+ caId=(buffer[4]<<8)+buffer[5];
+ LBSTARTF(L_CC_LOGIN);
+ char str[32], str2[32];
+ LBPUT("%s: CaID=%04x admin=%d srvUA=%s",name,caId,buffer[3]==1,HexStr(str,&buffer[6],8));
+ if(!CheckNull(&buffer[6],8)) {
+ emmProcessing=true;
+ switch(caId>>8) {
+ case 0x17:
+ case 0x06: SetCard(new cCardIrdeto(buffer[6+4],&buffer[6+5])); break;
+ case 0x01: SetCard(new cCardSeca(&buffer[6+2])); break;
+ case 0x0b: SetCard(new cCardConax(&buffer[6+1])); break;
+ case 0x09: SetCard(new cCardNDS(&buffer[6+4])); break;
+ case 0x05: SetCard(new cCardViaccess(&buffer[6+3])); break;
+ case 0x0d: SetCard(new cCardCryptoworks(&buffer[6+3])); break;
+ case 0x18: if(caId==0x1801) {
+ SetCard(new cCardNagra2(&buffer[6+4]));
+ break;
+ }
+ // fall through to default
+ default:
+ LBPUT(" (unhandled)");
+ break;
+ }
+ }
+ LBPUT(" provider");
+ for(int i=(buffer[14]-1)*11; i>=0; i-=11) {
+ LBPUT(" %s/%s",HexStr(str2,&buffer[15+i],3),HexStr(str,&buffer[18+i],8));
+ if(!CheckNull(&buffer[18+i],8)) {
+ switch(caId>>8) {
+ case 0x17:
+ case 0x06: AddProv(new cProviderIrdeto(buffer[18+i+4],&buffer[18+i+5])); break;
+ case 0x01: AddProv(new cProviderSeca(&buffer[15+i+1],&buffer[18+i+4])); break;
+ case 0x0b: AddProv(new cProviderConax(&buffer[18+i+1])); break;
+ case 0x09: AddProv(new cProviderNDS(&buffer[18+i+4])); break;
+ case 0x05: AddProv(new cProviderViaccess(&buffer[15+i],&buffer[18+i+4])); break;
+ case 0x0d: AddProv(new cProviderCryptoworks(&buffer[18+i+3])); break;
+ default:
+ LBPUT(" <unhandled>");
+ break;
+ }
+ }
+ }
+ LBEND();
+ if(emmProcessing && !emmAllowed)
+ PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+ }
+ return true;
+}
+
+bool cCardClientNewCamd::ProcessECM(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+ cMutexLock lock(this);
+ if((!so.Connected() && !Login()) || !CanHandle(ecm->caId)) return false;
+ so.Flush();
+
+ struct CustomData cd;
+ InitCustomData(&cd,(unsigned short)ecm->prgId,0);
+ if(!SendMessage(&so,data,SCT_LEN(data),true,&cd)) return false;
+ unsigned char buffer[CWS_NETMSGSIZE];
+ switch(ReceiveMessage(&so,buffer,true)) {
+ case 19: // ecm was decoded
+ // check for zero cw, as newcs doesn't send both cw's every time
+ if(!CheckNull(buffer+3+0,8)) memcpy(cw+0,buffer+3+0,8);
+ if(!CheckNull(buffer+3+8,8)) memcpy(cw+8,buffer+3+8,8);
+ return true;
+ case 3:
+ PRINTF(L_CC_ECM,"%s: card was not able to decode the channel",name);
+ break;
+ default:
+ PRINTF(L_CC_NEWCAMD,"warning an unexpected error occurred");
+ break;
+ }
+ return false;
+}
+
+bool cCardClientNewCamd::ProcessEMM(int caSys, const unsigned char *data)
+{
+ if(emmProcessing && emmAllowed) {
+ cMutexLock lock(this);
+ cAssembleData ad(data);
+ if(MatchAndAssemble(&ad,0,0)) {
+ while((data=ad.Assembled())) {
+ int len=SCT_LEN(data);
+ int id=msEMM.Get(data,len,0);
+ if(id>0) {
+ if(SendMessage(&so,data,len,true,0)) {
+ unsigned char buffer[CWS_NETMSGSIZE];
+ ReceiveMessage(&so,buffer,true);
+ }
+ msEMM.Cache(id,true,0);
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "cc.h"
+#include "parse.h"
+#include "network.h"
+#include "version.h"
+
+// -- cCardClientRadegast ------------------------------------------------------
+
+#define MAXCAIDS 16
+
+class cCardClientRadegast : public cCardClient, private cIdSet {
+private:
+ cNetSocket so;
+ bool emmProcessing;
+ int caids[MAXCAIDS], numCaids;
+ //
+ void InitVars(void);
+ void SetLength(unsigned char *buff, int len);
+ int GetLength(const unsigned char *buff);
+ int GetNanoStart(const unsigned char *buff);
+ int GetMsgLength(const unsigned char *buff);
+ int GetMaxLength(const unsigned char *buff);
+ void StartMsg(unsigned char *buff, int cmd);
+ bool CheckLength(const unsigned char *buff, int len);
+ void AddNano(unsigned char *buff, int nano, int len, int value);
+ void AddNano(unsigned char *buff, int nano, int len, const unsigned char *data);
+ bool Send(const unsigned char *buff);
+ int Recv(unsigned char *buff, int len);
+protected:
+ virtual bool Login(void);
+public:
+ cCardClientRadegast(const char *Name);
+ virtual bool Init(const char *config);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual bool ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw);
+ virtual bool ProcessEMM(int caSys, const unsigned char *data);
+ };
+
+static cCardClientLinkReg<cCardClientRadegast> __rdg("Radegast");
+
+cCardClientRadegast::cCardClientRadegast(const char *Name)
+:cCardClient(Name)
+,so(DEFAULT_CONNECT_TIMEOUT,7,DEFAULT_IDLE_TIMEOUT)
+{
+ InitVars();
+}
+
+void cCardClientRadegast::InitVars(void)
+{
+ emmProcessing=false; numCaids=0;
+}
+
+bool cCardClientRadegast::CanHandle(unsigned short SysId)
+{
+ cMutexLock lock(this);
+ if(numCaids<=0) return cCardClient::CanHandle(SysId);
+ for(int i=0; i<numCaids; i++) if(caids[i]==SysId) return true;
+ return false;
+}
+
+bool cCardClientRadegast::Init(const char *config)
+{
+ cMutexLock lock(this);
+ so.Disconnect();
+ return ParseStdConfig(config) && (Immediate() ? Login() : true);
+}
+
+void cCardClientRadegast::SetLength(unsigned char *buff, int len)
+{
+ if(buff[0]<=0x90) buff[1]=min(255,len);
+ else { buff[1]=len>>8; buff[2]=len&0xFF; }
+}
+
+int cCardClientRadegast::GetLength(const unsigned char *buff)
+{
+ return (buff[0]<=0x90) ? buff[1] : ((buff[1]<<8)+buff[2]);
+}
+
+int cCardClientRadegast::GetNanoStart(const unsigned char *buff)
+{
+ return (buff[0]<=0x90) ? 2 : 3;
+}
+
+int cCardClientRadegast::GetMsgLength(const unsigned char *buff)
+{
+ return GetLength(buff)+GetNanoStart(buff);
+}
+
+int cCardClientRadegast::GetMaxLength(const unsigned char *buff)
+{
+ return (buff[0]<=0x90) ? 0xFF : 0xFFFF;
+}
+
+void cCardClientRadegast::StartMsg(unsigned char *buff, int cmd)
+{
+ buff[0]=cmd;
+ SetLength(buff,0);
+}
+
+bool cCardClientRadegast::CheckLength(const unsigned char *buff, int len)
+{
+ int l=GetLength(buff)+len+2;
+ int max=GetMaxLength(buff);
+ if(len>255) PRINTF(L_CC_RDGD,"cmd %02x: nano too long %d > 255",buff[0],len);
+ else if(l>max) PRINTF(L_CC_RDGD,"cmd %02x: msg too long %d > %d",buff[0],l,max);
+ return l<=max;
+}
+
+void cCardClientRadegast::AddNano(unsigned char *buff, int nano, int len, int value)
+{
+ unsigned char hex[4];
+ for(int i=0; i<len; i++) hex[i]=value >> ((len-i-1)*8);
+ AddNano(buff,nano,len,hex);
+}
+
+void cCardClientRadegast::AddNano(unsigned char *buff, int nano, int len, const unsigned char *data)
+{
+ int pos=GetLength(buff), off=GetNanoStart(buff);
+ if(pos<GetMaxLength(buff)) {
+ buff[off+pos]=nano;
+ buff[off+pos+1]=len;
+ memcpy(&buff[off+pos+2],data,len);
+ pos+=len+2;
+ }
+ SetLength(buff,pos);
+}
+
+bool cCardClientRadegast::Send(const unsigned char *buff)
+{
+ return SendMsg(&so,buff,GetMsgLength(buff));
+}
+
+int cCardClientRadegast::Recv(unsigned char *buff, int len)
+{
+ int n=RecvMsg(&so,buff,len);
+ if(n<1) return n;
+ int k=GetNanoStart(buff);
+ if(n<k) {
+ PRINTF(L_CC_RDGD,"bad length %d < %d on cmd read",n,k);
+ return -1;
+ }
+ k=GetMsgLength(buff);
+ if(n<k) {
+ PRINTF(L_CC_RDGD,"bad length %d < %d on nano read",n,k);
+ return -1;
+ }
+ return n;
+}
+
+bool cCardClientRadegast::Login(void)
+{
+ so.Disconnect();
+ if(!so.Connect(hostname,port)) return false;
+ PRINTF(L_CC_LOGIN,"%s: connected to %s:%d",name,hostname,port);
+
+ InitVars();
+ unsigned char buff[512];
+ char hello[32];
+ snprintf(hello,sizeof(hello),"rdgd/vdr-sc-%s",ScVersion);
+ StartMsg(buff,0x90); // RDGD_MSG_CLIENT_HELLO
+ AddNano(buff,1,strlen(hello),(unsigned char *)hello); // RDGD_NANO_DESCR
+ int n;
+ if(!Send(buff) || (n=Recv(buff,sizeof(buff)))<0) return false;
+ if(n>0 && buff[0]==0x91) {
+ PRINTF(L_CC_RDGD,"got server hello, assuming V4 mode");
+ StartMsg(buff,0x94); // RDGD_MSG_CLIENT_CAP_REQ;
+ if(!Send(buff) || (n=Recv(buff,sizeof(buff)))<0) return false;
+ if(n>0 && buff[0]==0x95) {
+ LBSTARTF(L_CC_LOGIN);
+ LBPUT("radegast: got caps");
+ int caid;
+ for(int l=GetNanoStart(buff); l<n; l+=buff[l+1]+2) {
+ switch(buff[l]) {
+ case 0xE2:
+ LBPUT(" VERS %s",(char *)&buff[l+2]);
+ break;
+ case 0xE4: // CAP_NANO_CAID
+ if(numCaids>=MAXCAIDS) { l=n; break; } //stop processing
+ caid=(buff[l+2]<<8)+buff[l+3];
+ LBPUT(" CAID %04X",caid);
+ caids[numCaids++]=caid;
+ // XXX we should have EMM processing setup here, but as we don't
+ // XXX get any ua/sa we cannot filter EMM anyways
+ break;
+ case 0xE5: // CAP_NANO_PROVID
+ for(int i=0; i<buff[l+1]; i+=3)
+ LBPUT("/%02X%02X%02X",buff[l+2+i],buff[l+2+i+1],buff[l+2+i+2]);
+ break;
+ }
+ }
+ LBEND();
+ }
+ }
+ else PRINTF(L_CC_RDGD,"no server hello, assuming old mode");
+ if(emmProcessing && !emmAllowed)
+ PRINTF(L_CC_EMM,"%s: EMM disabled from config",name);
+ return true;
+}
+
+bool cCardClientRadegast::ProcessECM(const cEcmInfo *ecm, const unsigned char *source, unsigned char *cw)
+{
+ cMutexLock lock(this);
+ so.Flush();
+ int len=SCT_LEN(source);
+ int keynr=-1;
+ switch(ecm->caId>>8) {
+ case 0x01: // Seca
+ keynr=source[7]&0x0F; break;
+ case 0x05: // Viaccess
+ keynr=source[4]&0x0F; break;
+ }
+ unsigned char buff[512], tmp[10];
+ StartMsg(buff,1); // CMD_ECM_KEY_ASK
+ AddNano(buff,2,1,ecm->caId>>8); // ECM_NANO_CAID_INDEX (old)
+ AddNano(buff,10,2,ecm->caId); // ECM_NANO_CAID
+ sprintf((char *)tmp,"%08X",ecm->provId);
+ AddNano(buff,6,8,tmp); // ECM_NANO_PROVIDER
+ if(keynr>=0) {
+ sprintf((char *)tmp,"%04X",keynr);
+ AddNano(buff,7,4,tmp); // ECM_NANO_KEYNO
+ }
+ if(!CheckLength(buff,len)) return false;
+ AddNano(buff,3,len,source); // ECM_NANO_PACKET
+
+ if(!Send(buff) || (len=Recv(buff,sizeof(buff)))<=0) return false;
+ if(buff[0]==2) {
+ for(int l=GetNanoStart(buff); l<len; l+=buff[l+1]+2) {
+ switch(buff[l]) {
+ case 0x05:
+ if(buff[l+1]==16) {
+ // check for zero cw, as someone may not send both cw's every time
+ if(!CheckNull(buff+l+ 2,8)) memcpy(cw+0,buff+l+ 2,8);
+ if(!CheckNull(buff+l+10,8)) memcpy(cw+8,buff+l+10,8);
+ return true;
+ }
+ else PRINTF(L_CC_RDGD,"wrong cw length %d from server",buff[l+1]);
+ break;
+ case 0x04:
+ PRINTF(L_CC_ECM,"%s: key not available from server",name);
+ break;
+ default:
+ PRINTF(L_CC_RDGD,"unknown nano %02x in ECM response",buff[l]);
+ break;
+ }
+ }
+ }
+ else PRINTF(L_CC_RDGD,"bad ECM response from server %02x != 02",buff[0]);
+ return false;
+}
+
+bool cCardClientRadegast::ProcessEMM(int caSys, const unsigned char *data)
+{
+ if(emmProcessing && emmAllowed) {
+ cMutexLock lock(this);
+ int upd;
+ cProvider *p;
+ cAssembleData ad(data);
+ if(MatchAndAssemble(&ad,&upd,&p)) {
+ while((data=ad.Assembled())) {
+ int len=SCT_LEN(data);
+ int id=msEMM.Get(data,len,0);
+ if(id>0) {
+ unsigned char buff[512];
+ StartMsg(buff,0x41); //
+ AddNano(buff,0x42,2,caSys); // EMM_CAID
+ if(p) {
+ unsigned char tmp[10];
+ sprintf((char *)tmp,"%08X",(int)p->ProvId());
+ AddNano(buff,0x46,8,tmp); // EMM_PROVID
+ }
+/*
+ if(upd==2 || upd==3) {
+ AddNano(buff,0x44,2,(unsigned char *)"aa"); // EMM_ADDR_VAL
+ }
+*/
+ AddNano(buff,0x45,1,upd==3 ? 0x11 : (upd==2 ? 0x12 : 0x13)); // EMM_ADDR_TYPE
+ if(CheckLength(buff,len)) {
+ AddNano(buff,0x43,len,data); // EMM_CA_DATA
+ Send(buff);
+ }
+ msEMM.Cache(id,true,0);
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+
+#include "system.h"
+#include "system-common.h"
+#include "crypto.h"
+#include "data.h"
+#include "misc.h"
+#include "log-sys.h"
+
+#define SYSTEM_CONAX 0x0B00
+
+#define SYSTEM_NAME "Conax"
+#define SYSTEM_PRI -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_CONAX)
+
+#define L_SYS 3
+#define L_SYS_ALL LALL(L_SYS_LASTDEF)
+
+static const struct LogModule lm_sys = {
+ (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+ "conax",
+ { L_SYS_DEFNAMES }
+ };
+ADD_MODULE(L_SYS,lm_sys)
+
+// -- cSystemConax ---------------------------------------------------------------
+
+class cSystemConax : public cSystem {
+private:
+ cRSA rsa;
+ //
+ void ParseECM(unsigned char *buf, int len);
+public:
+ cSystemConax(void);
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
+ };
+
+cSystemConax::cSystemConax(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{}
+
+void cSystemConax::ParseECM(unsigned char *buf, int len)
+{
+ int i=0;
+ while(i<len) {
+ int nano=buf[i++];
+ int nanolen=buf[i++];
+
+ switch(nano) {
+ case 0x80:
+ case 0x82:
+ ParseECM(buf+i+3,nanolen-3); break;
+ case 0x81:
+ ParseECM(buf+i+2,nanolen-2); break;
+ case 0x30:
+ memcpy(cw ,buf+i+10,8);
+ memcpy(cw+8,buf+i+2 ,8);
+ break;
+ }
+ i+=nanolen;
+ }
+}
+
+bool cSystemConax::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+ int length=source[4]-2;
+ const int keyid=source[6];
+ const int nano=source[5];
+ source+=7;
+
+ cKeySnoop ks(this,'C',keyid,'M');
+ cPlainKey *pk;
+ cBN mod, exp;
+ if(!(pk=keys.FindKey('C',keyid,'E',-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %02X E key",keyid);
+ return false;
+ }
+ pk->Get(exp);
+ if(!(pk=keys.FindKey('C',keyid,'M',-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %02X M key",keyid);
+ return false;
+ }
+ pk->Get(mod);
+
+ for(int i=length ; i>0 ;) {
+ int index=i-64;
+ if(index<0) index=0;
+ if(rsa.RSA(source+index,source+index,64,exp,mod,false)<=0) {
+ if(doLog) PRINTF(L_SYS_CRYPTO,"RSA failed");
+ return false;
+ }
+ if(nano==0x63) {
+ // nano 0x63 block only decodes to 63 bytes
+ memmove(source+index,source+index+1,length-(index+1));
+ length-=1;
+ }
+ i-=(i%60 && index) ? i%60 : 64;
+ }
+
+ static const unsigned char hash[] = { 0x05,0x00,0x05 };
+ if(memcmp(hash,source+2,3) || memcmp(source+5,source+length-5,3)) {
+ if(doLog) PRINTF(L_SYS_ECM,"signature check failed");
+ return false;
+ }
+
+ ParseECM(source+10,length-10);
+ ks.OK(pk);
+ return true;
+}
+
+// -- cPlainKeyConax --------------------------------------------------------------
+
+#define PLAINLEN_CONAX 64
+
+class cPlainKeyConax : public cBNKey {
+protected:
+ virtual cString PrintKeyNr(void);
+public:
+ cPlainKeyConax(bool Super);
+ virtual bool Parse(const char *line);
+ };
+
+static cPlainKeyTypeReg<cPlainKeyConax,'C'> KeyReg;
+
+cPlainKeyConax::cPlainKeyConax(bool Super)
+:cBNKey(Super)
+{}
+
+bool cPlainKeyConax::Parse(const char *line)
+{
+ unsigned char sid, skey[PLAINLEN_CONAX];
+ const char *sline=line;
+ if(GetChar(line,&type,1) && GetHex(line,&sid,1) &&
+ GetChar(line,&keynr,1) && GetHex(line,skey,PLAINLEN_CONAX)) {
+ type=toupper(type); keynr=toupper(keynr); id=sid;
+ SetBinKey(skey,PLAINLEN_CONAX);
+ return true;
+ }
+ FormatError("conax",sline);
+ return false;
+}
+
+cString cPlainKeyConax::PrintKeyNr(void)
+{
+ return cString::sprintf("%c",keynr);
+}
+
+// -- cSystemLinkConax ---------------------------------------------------------
+
+class cSystemLinkConax : public cSystemLink {
+public:
+ cSystemLinkConax(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemConax; }
+ };
+
+static cSystemLinkConax staticInit;
+
+cSystemLinkConax::cSystemLinkConax(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkConax::CanHandle(unsigned short SysId)
+{
+ SysId&=SYSTEM_MASK;
+ return SYSTEM_CAN_HANDLE(SysId);
+}
--- /dev/null
+#
+# Conax
+#
+TARGET = conax
+OBJS = conax.o
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <malloc.h>
+#include <ctype.h>
+#include <typeinfo>
+
+#include <vdr/channels.h>
+#include <vdr/sources.h>
+
+#include "system.h"
+#include "system-common.h"
+#include "data.h"
+#include "misc.h"
+
+#define SYSTEM_NAME "ConstCW"
+#define SYSTEM_PRI -20
+
+// -- cPlainKeyConstCw ----------------------------------------------------------------
+
+#define PLAINLEN_CW 16
+
+class cPlainKeyConstCw : public cHexKey {
+private:
+ int prgId, source, transponder;
+ int freq;
+ char pol;
+protected:
+ virtual int IdSize(void) { return 4; }
+ virtual cString PrintKeyNr(void);
+public:
+ cPlainKeyConstCw(bool Super);
+ virtual bool Parse(const char *line);
+ bool Matches(const cEcmInfo *ecm);
+ };
+
+static cPlainKeyTypeReg<cPlainKeyConstCw,'X'> KeyReg;
+
+cPlainKeyConstCw::cPlainKeyConstCw(bool Super)
+:cHexKey(Super)
+{}
+
+bool cPlainKeyConstCw::Matches(const cEcmInfo *ecm)
+{
+ return ecm->prgId==prgId && ecm->source==source && ecm->transponder==transponder;
+}
+
+bool cPlainKeyConstCw::Parse(const char *line)
+{
+ unsigned char caid[2], skey[PLAINLEN_CW];
+ const char *sline=line;
+ if(GetChar(line,&type,1) && GetHex(line,caid,2)) {
+ int num;
+ char srcBuf[16];
+ if(sscanf(line," %d:%c:%15[^:]:%d%n",&freq,&pol,srcBuf,&prgId,&num)==4) {
+ source=cSource::FromString(srcBuf);
+ transponder=freq;
+ while(transponder>20000) transponder/=1000;
+ if(cSource::IsSat(source)) transponder=cChannel::Transponder(transponder,pol);
+ line+=num;
+ if(GetHex(line,skey,PLAINLEN_CW)) {
+ type=toupper(type);
+ id=Bin2Int(caid,2);
+ keynr=0;
+ SetBinKey(skey,PLAINLEN_CW);
+ return true;
+ }
+ }
+ }
+ FormatError("constcw",sline);
+ return false;
+}
+
+cString cPlainKeyConstCw::PrintKeyNr(void)
+{
+ return cString::sprintf("%d:%c:%s:%d",freq,pol,*cSource::ToString(source),prgId);
+}
+
+// -- cSystemConstCw ------------------------------------------------------------------
+
+class cSystemConstCw : public cSystem {
+public:
+ cSystemConstCw(void);
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
+ };
+
+cSystemConstCw::cSystemConstCw(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+ constant=true;
+}
+
+bool cSystemConstCw::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+ cKeySnoop ks(this,'X',ecm->caId,0);
+ cPlainKey *pk=0;
+ while((pk=keys.FindKey('X',ecm->caId,0,16,pk))) {
+ if(typeid(*pk)==typeid(cPlainKeyConstCw) && ((cPlainKeyConstCw *)pk)->Matches(ecm)) {
+ pk->Get(cw);
+ ks.OK(pk);
+ return true;
+ }
+ }
+ return false;
+}
+
+// -- cSystemLinkConstCw ------------------------------------------------------------
+
+class cSystemLinkConstCw : public cSystemLink {
+public:
+ cSystemLinkConstCw(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemConstCw; }
+ };
+
+static cSystemLinkConstCw staticInit;
+
+cSystemLinkConstCw::cSystemLinkConstCw(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkConstCw::CanHandle(unsigned short SysId)
+{
+ return keys.FindKeyNoTrig('X',SysId,0,16)!=0;
+}
--- /dev/null
+#
+# Const CW
+#
+TARGET = constcw
+OBJS = constcw.o
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <byteswap.h>
+
+#include "system-common.h"
+#include "data.h"
+#include "helper.h"
+#include "crypto.h"
+#include "misc.h"
+#include "log-sys.h"
+
+#define SYSTEM_CRYPTOWORKS 0x0D00
+
+#define SYSTEM_NAME "Cryptoworks"
+#define SYSTEM_PRI -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_CRYPTOWORKS)
+
+#define L_SYS 4
+#define L_SYS_ALL LALL(L_SYS_LASTDEF)
+
+static const struct LogModule lm_sys = {
+ (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+ "cryptoworks",
+ { L_SYS_DEFNAMES }
+ };
+ADD_MODULE(L_SYS,lm_sys)
+
+#define DUMPNANO(t,p,n) \
+ LBSTART(L_SYS_VERBOSE); \
+ LBPUT("%s",(t)); \
+ unsigned char *__p=(unsigned char *)(p); \
+ int __n=(n); \
+ for(int i=0; i<__n;) { \
+ LBFLUSH(); \
+ LBPUT("%02X %02X -",__p[i],__p[i+1]); \
+ i+=2; \
+ for(int l=__p[i-1]; l>0; l--) { LBPUT(" %02X",__p[i]); i++; }\
+ if(i>__n) { LBFLUSH(); LBPUT("length exceeded %d != %d",i,__n); } \
+ } \
+ LBEND();
+
+// -- cCwDes -------------------------------------------------------------------
+
+static const unsigned char cryptoPC1[] = {
+ 53,46,39,32,50,43,36,29,
+ 22,15, 8, 1,51,44,37,30,
+ 23,16, 9, 2,52,45,38,31,
+ 24,17,10, 3,53,46,39,32,
+ 25,18,11, 4,56,49,42,35,
+ 28,21,14, 7,55,48,41,34,
+ 27,20,13, 6,54,47,40,33,
+ 26,19,12, 5,25,18,11, 4
+ };
+
+static const unsigned char cryptoPC2[] = {
+ 18,21,15,28, 5, 9, 7,32,19,10,25,14,
+ 27,23,16, 8,30,12, 20,11,31,24,17, 6,
+ 49,60,39,45,55,63, 38,48,59,53,41,56,
+ 52,57,47,64,42,61, 54,50,58,44,37,40,
+ };
+
+#define shiftin(V,R,n) ((V<<1)+(((R)>>(n))&1))
+#define rol28(V,n) (V<<(n) | ((V&0x0fffffffL)>>(28-(n))))
+#define ror28(V,n) (V>>(n) | ((V&0xfffffff0L)<<(28-(n))))
+
+#define DESROUND(C,D,T) { \
+ unsigned int s=0; \
+ for(int j=7, k=0; j>=0; j--) { \
+ unsigned int v=0, K=0; \
+ for(int t=5; t>=0; t--, k++) { \
+ v=shiftin(v,T,E[k]); \
+ if(PC2[k]<33) K=shiftin(K,C,32-PC2[k]); \
+ else K=shiftin(K,D,64-PC2[k]); \
+ } \
+ s=(s<<4) + S[7-j][v^K]; \
+ } \
+ T=0; \
+ for(int j=31; j>=0; j--) T=shiftin(T,s,P[j]); \
+ }
+
+class cCwDes : public cDes {
+private:
+ void PrepKey(unsigned char *out, const unsigned char *key) const;
+ void PrepKey48(unsigned char *out, const unsigned char *key22, unsigned char algo) const;
+public:
+ cCwDes(void);
+ void CwDes(unsigned char *data, const unsigned char *key22, int mode) const;
+ void CwL2Des(unsigned char *data, const unsigned char *key22, unsigned char algo) const;
+ void CwR2Des(unsigned char *data, const unsigned char *key22, unsigned char algo) const;
+ };
+
+cCwDes::cCwDes(void)
+:cDes(cryptoPC1,cryptoPC2)
+{}
+
+void cCwDes::PrepKey(unsigned char *out, const unsigned char *key) const
+{
+ if(key!=out) memcpy(out,key,8);
+ Permute(out,PC1,64);
+}
+
+void cCwDes::PrepKey48(unsigned char *out, const unsigned char *key22, unsigned char algo) const
+{
+ memset(out,0,8);
+ memcpy(out,&key22[16],6);
+ int ctr=7-(algo&7);
+ for(int i=8; i>=2 && i>ctr; i--) out[i-2]=key22[i];
+}
+
+void cCwDes::CwDes(unsigned char *data, const unsigned char *key22, int mode) const
+{
+ unsigned char mkey[8];
+ PrepKey(mkey,key22+9);
+ LDUMP(L_SYS_VERBOSE,mkey,8,"prepped key DES:");
+ unsigned int C=UINT32_BE(mkey );
+ unsigned int D=UINT32_BE(mkey+4);
+ unsigned int L=UINT32_BE(data );
+ unsigned int R=UINT32_BE(data+4);
+ if(!(mode&DES_RIGHT)) {
+ for(int i=15; i>=0; i--) {
+ C=rol28(C,LS[15-i]); D=rol28(D,LS[15-i]);
+ unsigned int T=R;
+ DESROUND(C,D,T);
+ T^=L; L=R; R=T;
+ }
+ }
+ else {
+ for(int i=15; i>=0; i--) {
+ unsigned int T=R;
+ DESROUND(C,D,T);
+ T^=L; L=R; R=T;
+ C=ror28(C,LS[i]); D=ror28(D,LS[i]);
+ }
+ }
+ BYTE4_BE(data ,L);
+ BYTE4_BE(data+4,R);
+}
+
+void cCwDes::CwL2Des(unsigned char *data, const unsigned char *key22, unsigned char algo) const
+{
+ unsigned char mkey[8];
+ PrepKey48(mkey,key22,algo);
+ PrepKey(mkey,mkey);
+ LDUMP(L_SYS_VERBOSE,mkey,8,"prepped key L2:");
+ unsigned int C=UINT32_BE(mkey );
+ unsigned int D=UINT32_BE(mkey+4);
+ unsigned int L=UINT32_BE(data );
+ unsigned int R=UINT32_BE(data+4);
+ for(int i=1; i>=0; i--) {
+ C=rol28(C,1); D=rol28(D,1);
+ unsigned int T=R;
+ DESROUND(C,D,T);
+ T^=L; L=R; R=T;
+ PRINTF(L_SYS_VERBOSE,"round %d key L2: %08x %08x",i,C,D);
+ }
+ BYTE4_BE(data ,L);
+ BYTE4_BE(data+4,R);
+}
+
+void cCwDes::CwR2Des(unsigned char *data, const unsigned char *key22, unsigned char algo) const
+{
+ unsigned char mkey[8];
+ PrepKey48(mkey,key22,algo);
+ PrepKey(mkey,mkey);
+ LDUMP(L_SYS_VERBOSE,mkey,8,"prepped key R2:");
+ unsigned int C=UINT32_BE(mkey );
+ unsigned int D=UINT32_BE(mkey+4);
+ unsigned int L=UINT32_BE(data );
+ unsigned int R=UINT32_BE(data+4);
+ for(int i=1; i>=0; i--) {
+ C=rol28(C,15); D=rol28(D,15);
+ }
+ for(int i=1; i>=0; i--) {
+ unsigned int T=R;
+ DESROUND(C,D,T);
+ T^=L; L=R; R=T;
+ C=ror28(C,1); D=ror28(D,1);
+ }
+ BYTE4_BE(data ,R);
+ BYTE4_BE(data+4,L);
+}
+
+// -- cCryptoworks -------------------------------------------------------------
+
+class cCryptoworks {
+private:
+ cCwDes des;
+ cRSA rsa;
+ cBN exp;
+ //
+ void EncDec(unsigned char *data, const unsigned char *key22, unsigned char algo, int mode);
+public:
+ cCryptoworks(void);
+ void Signature(const unsigned char *data, int len, const unsigned char *key22, unsigned char *sig);
+ void DecryptDES(unsigned char *data, unsigned char algo, const unsigned char *key22);
+ bool DecryptRSA(unsigned char *data, int len, unsigned char algo, const unsigned char *key22, BIGNUM *mod);
+ };
+
+cCryptoworks::cCryptoworks(void)
+{
+ BN_set_word(exp,2);
+}
+
+void cCryptoworks::EncDec(unsigned char *data, const unsigned char *key22, unsigned char algo, int mode)
+{
+ LDUMP(L_SYS_VERBOSE,data,8,"encdec in :");
+ PRINTF(L_SYS_VERBOSE,"algo %d",algo);
+ LDUMP(L_SYS_VERBOSE,key22,22,"encdec key:");
+ des.CwL2Des(data,key22,algo);
+ des.CwDes(data,key22,mode);
+ des.CwR2Des(data,key22,algo);
+ LDUMP(L_SYS_VERBOSE,data,8,"encdec out:");
+}
+
+void cCryptoworks::Signature(const unsigned char *data, int len, const unsigned char *key22, unsigned char *sig)
+{
+ PRINTF(L_SYS_VERBOSE,"sig start");
+ int algo=data[0]&7;
+ if(algo==7) algo=6;
+ memset(sig,0,8);
+ int j=0;
+ bool first=true;
+ for(int i=0; i<len; i++) {
+ sig[j]^=data[i];
+ if(++j>7) {
+ LDUMP(L_SYS_VERBOSE,sig,8,"sig buf");
+ if(first) {
+ des.CwL2Des(sig,key22,algo);
+ LDUMP(L_SYS_VERBOSE,sig,8,"sig -> ");
+ }
+ des.CwDes(sig,key22,DES_LEFT);
+ j=0; first=false;
+ LDUMP(L_SYS_VERBOSE,sig,8,"sig -> ");
+ }
+ }
+ if(j>0) {
+ LDUMP(L_SYS_VERBOSE,sig,8,"sig buf final ");
+ des.CwDes(sig,key22,DES_LEFT);
+ LDUMP(L_SYS_VERBOSE,sig,8,"sig -> ");
+ }
+ des.CwR2Des(sig,key22,algo);
+ LDUMP(L_SYS_VERBOSE,sig,8,"sig calc ");
+}
+
+void cCryptoworks::DecryptDES(unsigned char *data, unsigned char algo, const unsigned char *key22)
+{
+ algo&=7;
+ if(algo<7) {
+ EncDec(data,key22,algo,DES_RIGHT);
+ }
+ else {
+ unsigned char k[22], t[8];
+ memcpy(k,key22,22);
+ for(int i=0; i<3; i++) {
+ EncDec(data,k,algo,i&1);
+ memcpy(t,k,8); memcpy(k,k+8,8); memcpy(k+8,t,8);
+ }
+ }
+}
+
+bool cCryptoworks::DecryptRSA(unsigned char *data, int len, unsigned char algo, const unsigned char *key22, BIGNUM *mod)
+{
+ unsigned char buf[64], mask[len];
+
+ LDUMP(L_SYS_VERBOSE,data+len,8,"rsa in:");
+ memcpy(buf,data+len,8);
+ EncDec(buf,key22,algo,DES_LEFT);
+ buf[0]|=0x80;
+ if((algo&0x18)<0x18) buf[0]=0xFF;
+ if(algo&8) buf[1]=0xFF;
+ LDUMP(L_SYS_VERBOSE,buf,8,"rsa seed:");
+
+ static const unsigned char t1[] = { 0xE,0x3,0x5,0x8,0x9,0x4,0x2,0xF,0x0,0xD,0xB,0x6,0x7,0xA,0xC,0x1 };
+ for(int k=0; k<len; k+=32) {
+ memcpy(buf+8,buf,8);
+ for(int i=0; i<8; i++) {
+ int n=i<<1;
+ buf[n+1]=buf[i+8];
+ buf[n ]=(t1[buf[n+1]>>4]<<4) | t1[buf[i+8]&0xF];
+ LDUMP(L_SYS_VERBOSE,buf,16,"rsa buf:");
+ }
+ for(int i=16; i<64; i+=16) memcpy(&buf[i],buf,16);
+ buf[31]=((buf[15]<<4)&0xFF) | 6;
+ buf[16]=buf[0]^1;
+ buf[32]&=0x7F;
+ buf[32]|=0x40;
+ RotateBytes(buf,32);
+ RotateBytes(buf+32,32);
+ LDUMP(L_SYS_VERBOSE,buf,64,"rsa data:");
+
+ if(rsa.RSA(buf,buf,64,exp,mod,true)==0) {
+ PRINTF(L_SYS_CRYPTO,"RSA failed");
+ return false;
+ }
+ RotateBytes(buf,8);
+ RotateBytes(mask+k,buf+8,min(32,len-k));
+ }
+ LDUMP(L_SYS_VERBOSE,mask,len,"rsa out:");
+
+ xxor(data,len,data,mask);
+ return true;
+}
+
+// -- cPlainKeyCryptoworks -----------------------------------------------------
+
+#define PLAINLEN_CW_D 16
+#define PLAINLEN_CW_CC 6
+#define PLAINLEN_CW_R 64
+
+#define CCTYP 0x00
+#define CCID 0xFF
+
+#define PROV(keynr) (((keynr)>>16)&0xFF)
+#define TYPE(keynr) (((keynr)>> 8)&0xFF)
+#define ID(keynr) (((keynr) )&0xFF)
+#define KEYSET(prov,typ,id) ((((prov)&0xFF)<<16)|(((typ)&0xFF)<<8)|((id)&0xFF))
+
+class cPlainKeyCryptoworks : public cDualKey {
+protected:
+ virtual bool IsBNKey(void) const;
+ virtual int IdSize(void) { return 4; }
+ virtual cString PrintKeyNr(void);
+public:
+ cPlainKeyCryptoworks(bool Super);
+ virtual bool Parse(const char *line);
+ };
+
+static cPlainKeyTypeReg<cPlainKeyCryptoworks,'W'> KeyReg;
+
+cPlainKeyCryptoworks::cPlainKeyCryptoworks(bool Super)
+:cDualKey(Super,true)
+{}
+
+bool cPlainKeyCryptoworks::IsBNKey(void) const
+{
+ return TYPE(keynr)==0x10;
+}
+
+bool cPlainKeyCryptoworks::Parse(const char *line)
+{
+ const char *sline=line;
+ unsigned char sid[2], sprov;
+ if(GetChar(line,&type,1) && GetHex(line,sid,2) && GetHex(line,&sprov,1)) {
+ int keylen, prov;
+ type=toupper(type); id=Bin2Int(sid,2); prov=sprov;
+ line=skipspace(line);
+ bool ok;
+ if(!strncasecmp(line,"CC",2)) { // cardkey
+ keynr=KEYSET(prov,CCTYP,CCID);
+ keylen=PLAINLEN_CW_CC;
+ line+=2;
+ ok=true;
+ }
+ else {
+ unsigned char sid, styp;
+ ok=GetHex(line,&styp,1) && GetHex(line,&sid,1);
+ keynr=KEYSET(prov,styp,sid);
+ keylen=IsBNKey() ? PLAINLEN_CW_R : PLAINLEN_CW_D;
+ }
+ if(ok) {
+ unsigned char skey[keylen];
+ if(GetHex(line,skey,keylen)) {
+ SetBinKey(skey,keylen);
+ return true;
+ }
+ }
+ }
+ FormatError("cryptoworks",sline);
+ return false;
+}
+
+cString cPlainKeyCryptoworks::PrintKeyNr(void)
+{
+ int prov=PROV(keynr);
+ int keytyp=TYPE(keynr);
+ int keyid=ID(keynr);
+ return cString::sprintf(keytyp==CCTYP && keyid==CCID ? "%02X CC":"%02X %02X %02X",prov,keytyp,keyid);
+}
+
+// -- cSystemCryptoworks -------------------------------------------------------
+
+#define ECM_ALGO_TYP 5
+#define ECM_DATA_START ECM_ALGO_TYP
+#define ECM_NANO_LEN 7
+#define ECM_NANO_START 8
+
+class cSystemCryptoworks : public cSystem, private cCryptoworks {
+private:
+public:
+ cSystemCryptoworks(void);
+ virtual bool ProcessECM(const cEcmInfo *ecmInfo, unsigned char *data);
+// virtual void ProcessEMM(int pid, int caid, unsigned char *data);
+ };
+
+cSystemCryptoworks::cSystemCryptoworks(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+// hasLogger=true;
+}
+
+bool cSystemCryptoworks::ProcessECM(const cEcmInfo *ecmInfo, unsigned char *data)
+{
+ int len=SCT_LEN(data);
+ if(data[ECM_NANO_LEN]!=len-ECM_NANO_START) {
+ PRINTF(L_SYS_ECM,"invalid ECM structure");
+ return false;
+ }
+
+ int prov=-1, keyid=0;
+ for(int i=ECM_NANO_START; i<len; i+=data[i+1]+2) {
+ if(data[i]==0x83) {
+ prov =data[i+2]&0xFC;
+ keyid=data[i+2]&0x03;
+ break;
+ }
+ }
+ if(prov<0) {
+ PRINTF(L_SYS_ECM,"provider ID not located in ECM");
+ return false;
+ }
+
+ unsigned char key[22];
+ cPlainKey *pk;
+ if(!(pk=keys.FindKey('W',ecmInfo->caId,KEYSET(prov,CCTYP,CCID),6))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04X %02X CC key",ecmInfo->caId,prov);
+ return false;
+ }
+ pk->Get(key+16);
+
+ // RSA stage
+ DUMPNANO("pre-RSA",&data[ECM_NANO_START],len-ECM_NANO_START);
+ for(int i=ECM_NANO_START; i<len; i+=data[i+1]+2) {
+ int l=data[i+1]+2;
+ switch(data[i]) {
+ case 0x85:
+ {
+ if(!(pk=keys.FindKey('W',ecmInfo->caId,KEYSET(prov,0x31,keyid),16))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04X %02X 31 %02X key",ecmInfo->caId,prov,keyid);
+ return false;
+ }
+ pk->Get(key);
+ cBN mod;
+ if(!(pk=keys.FindKey('W',ecmInfo->caId,KEYSET(prov,0x10,0x00),64))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04X %02X 10 00 key",ecmInfo->caId,prov);
+ return false;
+ }
+ pk->Get(mod);
+
+ l-=10;
+ if(!DecryptRSA(&data[i+2],l,data[ECM_ALGO_TYP],key,mod))
+ return false;
+ memmove(&data[i],&data[i+2],l);
+ memmove(&data[i+l],&data[i+l+10],len-i-l);
+ len-=10;
+ break;
+ }
+ case 0x86:
+ memmove(&data[i],&data[i+l],len-i-l);
+ len-=l;
+ continue;
+ }
+ }
+ DUMPNANO("post-RSA",&data[ECM_NANO_START],len-ECM_NANO_START);
+
+ cKeySnoop ks(this,'W',ecmInfo->caId,KEYSET(prov,0x20,keyid));
+ if(!(pk=keys.FindKey('W',ecmInfo->caId,KEYSET(prov,0x20,keyid),16))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04X %02X 20 %02X key",ecmInfo->caId,prov,keyid);
+ return false;
+ }
+ pk->Get(key);
+
+ // DES stage
+ unsigned char sig[8];
+ LDUMP(L_SYS_VERBOSE,&data[len-8],8,"sig org:");
+ data[ECM_NANO_LEN]=len-ECM_NANO_START;
+ Signature(&data[ECM_DATA_START],len-ECM_DATA_START-10,key,sig);
+ for(int i=ECM_NANO_START; i<len; i+=data[i+1]+2) {
+ switch(data[i]) {
+ case 0xDA:
+ case 0xDB:
+ case 0xDC:
+ for(int j=0; j<data[i+1]; j+=8)
+ DecryptDES(&data[i+2+j],data[ECM_ALGO_TYP],key);
+ break;
+ case 0xDF:
+ if(memcmp(&data[i+2],sig,8)) {
+ PRINTF(L_SYS_ECM,"signature failed in ECM");
+ return false;
+ }
+ break;
+ }
+ }
+
+ // CW stage
+ for(int i=ECM_NANO_START; i<len; i+=data[i+1]+2) {
+ switch(data[i]) {
+ case 0xDB:
+ if(data[i+1]==0x10) {
+ memcpy(cw,&data[i+2],16);
+ LDUMP(L_SYS_VERBOSE,cw,16,"cw:");
+ ks.OK(pk);
+ return true;
+ }
+ break;
+ }
+ }
+
+ return false;
+}
+
+/*
+void cSystemCryptoworks::ProcessEMM(int pid, int caid, unsigned char *data)
+{
+}
+*/
+
+// -- cSystemLinkCryptoworks ---------------------------------------------------
+
+class cSystemLinkCryptoworks : public cSystemLink {
+public:
+ cSystemLinkCryptoworks(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemCryptoworks; }
+ };
+
+static cSystemLinkCryptoworks staticInit;
+
+cSystemLinkCryptoworks::cSystemLinkCryptoworks(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkCryptoworks::CanHandle(unsigned short SysId)
+{
+ SysId&=SYSTEM_MASK;
+ return SYSTEM_CAN_HANDLE(SysId);
+}
--- /dev/null
+#
+# Cryptoworks
+#
+TARGET = cryptoworks
+OBJS = cryptoworks.o
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "data.h"
+#include "parse.h"
+#include "misc.h"
+#include "log-sys.h"
+
+#define SYSTEM_IRDETO 0x0600
+#define SYSTEM_BETA 0x1700
+
+#define SYSTEM_NAME "Irdeto"
+#define SYSTEM_PRI -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_IRDETO || (x)==SYSTEM_BETA)
+
+#define L_SYS 5
+#define L_SYS_ALL LALL(L_SYS_LASTDEF)
+
+static const struct LogModule lm_sys = {
+ (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+ "irdeto",
+ { L_SYS_DEFNAMES }
+ };
+ADD_MODULE(L_SYS,lm_sys)
+
+static cPlainKeyTypeReg<cPlainKeyStd,'I',false> KeyReg;
+
+// -- cIrdCardInfo -------------------------------------------------------------
+
+class cIrdCardInfo : public cProviderIrdeto, public cCardIrdeto {
+private:
+ bool updated;
+public:
+ unsigned char PMK[8], HMK[10];
+ bool haveHMK;
+ //
+ cIrdCardInfo(void);
+ bool Parse(const char *line);
+ bool Save(FILE *f);
+ bool Update(unsigned char *pmk, unsigned char *id);
+ bool IsUpdated(void) { return updated; }
+ void Updated(void) { updated=true; }
+ bool Cmp(cIrdCardInfo *ci);
+ };
+
+cIrdCardInfo::cIrdCardInfo(void)
+{
+ updated=false;
+}
+
+bool cIrdCardInfo::Parse(const char *line)
+{
+ haveHMK=false;
+ if(GetHex(line,hexSer,sizeof(hexSer)) && GetHex(line,HMK,sizeof(HMK))) haveHMK=true;
+ return GetHex(line,&provBase,sizeof(provBase)) &&
+ GetHex(line,provId,sizeof(provId)) &&
+ GetHex(line,PMK,sizeof(PMK));
+}
+
+bool cIrdCardInfo::Save(FILE *f)
+{
+ char s1[20], s2[20], s3[20], s4[20];
+ fprintf(f,"%s %s %02x %s %s\n",
+ HexStr(s1,hexSer,sizeof(hexSer)),HexStr(s2,HMK,sizeof(HMK)),provBase,
+ HexStr(s3,provId,sizeof(provId)),HexStr(s4,PMK,sizeof(PMK)));
+ return ferror(f)==0;
+}
+
+bool cIrdCardInfo::Cmp(cIrdCardInfo *ci)
+{
+ return (!memcmp(hexSer,ci->hexSer,sizeof(hexSer)) &&
+ !memcmp(HMK,ci->HMK,sizeof(HMK)));
+}
+
+bool cIrdCardInfo::Update(unsigned char *pmk, unsigned char *id)
+{
+ bool res=false;
+ char str[20], str2[12];
+ if(memcmp(PMK,pmk,sizeof(PMK))) {
+ PRINTF(L_GEN_INFO,"new PMK for I card %s: %s",HexStr(str2,hexSer,sizeof(hexSer)),KeyStr(str,pmk));
+ memcpy(PMK,pmk,sizeof(PMK));
+ Updated(); res=true;
+ }
+ if(id && memcmp(provId,id,sizeof(provId))) {
+ PRINTF(L_GEN_INFO,"new PrvID for I card %s: %s",HexStr(str2,hexSer,sizeof(hexSer)),HexStr(str,id,sizeof(provId)));
+ memcpy(provId,id,sizeof(provId));
+ Updated(); res=true;
+ }
+ return res;
+}
+
+// -- cIrdCardInfos ------------------------------------------------------------
+
+class cIrdCardInfos : public cCardInfos<cIrdCardInfo> {
+public:
+ cIrdCardInfos(void):cCardInfos<cIrdCardInfo>(SYSTEM_NAME) {}
+ bool Update(cIrdCardInfo *ci, unsigned char *pmk, unsigned char *id);
+ };
+
+static cIrdCardInfos Icards;
+
+bool cIrdCardInfos::Update(cIrdCardInfo *ci, unsigned char *pmk, unsigned char *id)
+{
+ bool res=ci->Update(pmk,id);
+ if(res) Modified();
+ return res;
+}
+
+// -- cIrdeto ------------------------------------------------------------------
+
+class cIrdeto {
+private:
+ static const unsigned char T0[], T1[], T2[];
+ unsigned char tempKey[10];
+ //
+ void Rotate(unsigned char *p);
+ void MakeTempKey(const unsigned char *key, int date);
+protected:
+ void SessionKeyCrypt(unsigned char *data, const unsigned char *key, int date);
+ bool SignatureCheck(const unsigned char *data, int length, const unsigned char *key, int date, const unsigned char *signature, int keylen);
+ void DecryptIrd(unsigned char *data, unsigned char *key, int rounds, int offset);
+ };
+
+const unsigned char cIrdeto::T0[32] = {
+ 0,1,2,3,4,5,6,7, 0,1,2,3,4,5,6,7,
+ 0,1,2,3,4,5,6,7, 0,3,6,1,4,7,2,5
+ };
+
+const unsigned char cIrdeto::T1[256] = {
+ 0xDA,0x26,0xE8,0x72,0x11,0x52,0x3E,0x46, 0x32,0xFF,0x8C,0x1E,0xA7,0xBE,0x2C,0x29,
+ 0x5F,0x86,0x7E,0x75,0x0A,0x08,0xA5,0x21, 0x61,0xFB,0x7A,0x58,0x60,0xF7,0x81,0x4F,
+ 0xE4,0xFC,0xDF,0xB1,0xBB,0x6A,0x02,0xB3, 0x0B,0x6E,0x5D,0x5C,0xD5,0xCF,0xCA,0x2A,
+ 0x14,0xB7,0x90,0xF3,0xD9,0x37,0x3A,0x59, 0x44,0x69,0xC9,0x78,0x30,0x16,0x39,0x9A,
+ 0x0D,0x05,0x1F,0x8B,0x5E,0xEE,0x1B,0xC4, 0x76,0x43,0xBD,0xEB,0x42,0xEF,0xF9,0xD0,
+ 0x4D,0xE3,0xF4,0x57,0x56,0xA3,0x0F,0xA6, 0x50,0xFD,0xDE,0xD2,0x80,0x4C,0xD3,0xCB,
+ 0xF8,0x49,0x8F,0x22,0x71,0x84,0x33,0xE0, 0x47,0xC2,0x93,0xBC,0x7C,0x3B,0x9C,0x7D,
+ 0xEC,0xC3,0xF1,0x89,0xCE,0x98,0xA2,0xE1, 0xC1,0xF2,0x27,0x12,0x01,0xEA,0xE5,0x9B,
+ 0x25,0x87,0x96,0x7B,0x34,0x45,0xAD,0xD1, 0xB5,0xDB,0x83,0x55,0xB0,0x9E,0x19,0xD7,
+ 0x17,0xC6,0x35,0xD8,0xF0,0xAE,0xD4,0x2B, 0x1D,0xA0,0x99,0x8A,0x15,0x00,0xAF,0x2D,
+ 0x09,0xA8,0xF5,0x6C,0xA1,0x63,0x67,0x51, 0x3C,0xB2,0xC0,0xED,0x94,0x03,0x6F,0xBA,
+ 0x3F,0x4E,0x62,0x92,0x85,0xDD,0xAB,0xFE, 0x10,0x2E,0x68,0x65,0xE7,0x04,0xF6,0x0C,
+ 0x20,0x1C,0xA9,0x53,0x40,0x77,0x2F,0xA4, 0xFA,0x6D,0x73,0x28,0xE2,0xCD,0x79,0xC8,
+ 0x97,0x66,0x8E,0x82,0x74,0x06,0xC7,0x88, 0x1A,0x4A,0x6B,0xCC,0x41,0xE9,0x9D,0xB8,
+ 0x23,0x9F,0x3D,0xBF,0x8D,0x95,0xC5,0x13, 0xB9,0x24,0x5A,0xDC,0x64,0x18,0x38,0x91,
+ 0x7F,0x5B,0x70,0x54,0x07,0xB6,0x4B,0x0E, 0x36,0xAC,0x31,0xE6,0xD6,0x48,0xAA,0xB4
+ };
+
+const unsigned char cIrdeto::T2[256] = {
+ 0x8E,0xD5,0x32,0x53,0x4B,0x18,0x7F,0x95, 0xBE,0x30,0xF3,0xE0,0x22,0xE1,0x68,0x90,
+ 0x82,0xC8,0xA8,0x57,0x21,0xC5,0x38,0x73, 0x61,0x5D,0x5A,0xD6,0x60,0xB7,0x48,0x70,
+ 0x2B,0x7A,0x1D,0xD1,0xB1,0xEC,0x7C,0xAA, 0x2F,0x1F,0x37,0x58,0x72,0x88,0xFF,0x87,
+ 0x1C,0xCB,0x00,0xE6,0x4E,0xAB,0xEB,0xB3, 0xF7,0x59,0x71,0x6A,0x64,0x2A,0x55,0x4D,
+ 0xFC,0xC0,0x51,0x01,0x2D,0xC4,0x54,0xE2, 0x9F,0x26,0x16,0x27,0xF2,0x9C,0x86,0x11,
+ 0x05,0x29,0xA2,0x78,0x49,0xB2,0xA6,0xCA, 0x96,0xE5,0x33,0x3F,0x46,0xBA,0xD0,0xBB,
+ 0x5F,0x84,0x98,0xE4,0xF9,0x0A,0x62,0xEE, 0xF6,0xCF,0x94,0xF0,0xEA,0x1E,0xBF,0x07,
+ 0x9B,0xD9,0xE9,0x74,0xC6,0xA4,0xB9,0x56, 0x3E,0xDB,0xC7,0x15,0xE3,0x80,0xD7,0xED,
+ 0xEF,0x13,0xAC,0xA1,0x91,0xC2,0x89,0x5B, 0x08,0x0B,0x4C,0x02,0x3A,0x5C,0xA9,0x3B,
+ 0xCE,0x6B,0xA7,0xE7,0xCD,0x7B,0xA0,0x47, 0x09,0x6D,0xF8,0xF1,0x8B,0xB0,0x12,0x42,
+ 0x4A,0x9A,0x17,0xB4,0x7E,0xAD,0xFE,0xFD, 0x2C,0xD3,0xF4,0xB6,0xA3,0xFA,0xDF,0xB8,
+ 0xD4,0xDA,0x0F,0x50,0x93,0x66,0x6C,0x20, 0xD8,0x8A,0xDD,0x31,0x1A,0x8C,0x06,0xD2,
+ 0x44,0xE8,0x23,0x43,0x6E,0x10,0x69,0x36, 0xBC,0x19,0x8D,0x24,0x81,0x14,0x40,0xC9,
+ 0x6F,0x2E,0x45,0x52,0x41,0x92,0x34,0xFB, 0x5E,0x0D,0xF5,0x76,0x25,0x77,0x63,0x65,
+ 0xAF,0x4F,0xCC,0x03,0x9D,0x0C,0x28,0x39, 0x85,0xDE,0xB5,0x7D,0x67,0x83,0xBD,0xC3,
+ 0xDC,0x3C,0xAE,0x99,0x04,0x75,0x8F,0x97, 0xC1,0xA5,0x9E,0x35,0x0E,0x3D,0x1B,0x79
+ };
+
+void cIrdeto::Rotate(unsigned char *p)
+{
+ unsigned char c, t;
+ c=p[9]<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+ t=*p; *p++=(t>>1) | c; c=t<<7;
+}
+
+void cIrdeto::MakeTempKey(const unsigned char *key, int date)
+{
+ memcpy(tempKey,key,8);
+ tempKey[8]=(unsigned char)(date >> 8) ^ key[0];
+ tempKey[9]=(unsigned char)date ^ key[1];
+}
+
+void cIrdeto::DecryptIrd(unsigned char *data, unsigned char *key, int rounds, int offset)
+{
+ for(int count=0; count<rounds; count++) {
+ int index=count % 10;
+ if(index==0) Rotate(key);
+ unsigned char k=key[index];
+ unsigned char d=data[T0[(count & 0x0f) + offset]] ^ k;
+ d=(k&1) ? T1[d] : T2[d];
+ data[T0[((count+1) & 0x0f) + offset]] ^= d;
+ }
+}
+
+void cIrdeto::SessionKeyCrypt(unsigned char *data, const unsigned char *key, int date)
+{
+ MakeTempKey(key,date);
+ DecryptIrd(data,tempKey,128,16);
+}
+
+bool cIrdeto::SignatureCheck(const unsigned char *data, int length, const unsigned char *key, int date, const unsigned char *signature, int keylen)
+{
+ unsigned char buffer[256];
+ memcpy(buffer,data,length);
+ int rounds;
+ if((length%8)==0) rounds=40;
+ else {
+ int i=0x61;
+ while((length%8)!=0) buffer[length++]=i++;
+ rounds=104;
+ }
+
+ if(keylen==10) memcpy(tempKey,key,10);
+ else MakeTempKey(key,date);
+
+ int i=0;
+ while(i<length-8) {
+ DecryptIrd(buffer,tempKey,40,0);
+ i+=8;
+ for(int j=0; j<8; j++) buffer[j] ^= buffer[i+j];
+ Rotate(tempKey);
+ }
+ DecryptIrd(buffer,tempKey,rounds,0);
+ return memcmp(buffer,signature,5)==0;
+}
+
+// -- cSystemIrd ---------------------------------------------------------------
+
+class cSystemIrd : public cSystem, private cIrdeto {
+private:
+ unsigned char lastKey;
+public:
+ cSystemIrd(void);
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
+ virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+ };
+
+cSystemIrd::cSystemIrd(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+ lastKey=0;
+ hasLogger=true;
+}
+
+bool cSystemIrd::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+ unsigned char *data=0;
+ int date=-1, keynr=0;
+ source+=6;
+ int length=source[5]+6-5; // 6 header bytes - 5 signature bytes
+ for(int index=6 ; index<length && (data==0 || date==-1) ;) {
+ int param=source[index++];
+ int len =source[index++] & 0x3f;
+
+ switch(param) {
+ case 0x78:
+ keynr = source[index];
+ data = &source[index+2];
+ break;
+ case 0x00:
+ case 0x40:
+ date = (source[index]<<8) | source[index+1];
+ break;
+ }
+ index+=len;
+ }
+ if(data==0 || date==-1) {
+ PRINTF(L_SYS_ECM,"incomplete ECM structure");
+ return false;
+ }
+
+ cKeySnoop ks(this,'I',source[2],keynr);
+ cPlainKey *pk=0;
+ unsigned char key[8];
+ while((pk=keys.FindKey('I',source[2],keynr,sizeof(key),pk))) {
+ unsigned char save[16];
+ memcpy(save,data,16); // save the encrypted data
+ pk->Get(key);
+ SessionKeyCrypt(&data[0],key,date);
+ SessionKeyCrypt(&data[8],key,date);
+ if(SignatureCheck(source,length,key,date,&source[length],8)) {
+ memcpy(cw,&data[0],16);
+ ks.OK(pk);
+ return true;
+ }
+ memcpy(data,save,16); // put back the encrypted data if it didn't works
+ }
+ return false;
+}
+
+void cSystemIrd::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+ int i, numKeys=0, date=0;
+ unsigned char adr[10], id[4], *pk[4], prov, *mk=0, prvId[3]={0,0,0};
+
+ int n=SCT_LEN(buffer);
+ unsigned char savebuf[4096];
+ if(n>(int)sizeof(savebuf)) {
+ PRINTF(L_SYS_EMM,"%d: paket size %d to big for savebuffer in IrdetoLog",CardNum(),n);
+ return;
+ }
+
+ int index=cParseIrdeto::AddrLen(buffer);
+ memset(adr,0,sizeof(adr));
+ memcpy(adr,&buffer[3],index+1);
+ index+=4+4; // 3 header + adr type + 4 {0x01 0x00 0x00 len}
+
+ int sindex=index; // save index for sig. check
+ int slen=buffer[index-1]-5; // save packet length, 5 signature bytes
+ int maxIndex=index+slen;
+ if(maxIndex>n-5) {
+ PRINTF(L_SYS_EMM,"%d: bad packet length (%d > %d)",CardNum(),maxIndex,n-5);
+ maxIndex=n-5;
+ }
+ bool badnano=false;
+ while(!badnano && index<maxIndex) {
+ unsigned char nlen=buffer[index+1] & 0x3F;
+ //unsigned char prio=buffer[index] & 0x40;
+ unsigned char nano=buffer[index] & ~0x40;
+ switch(nano) {
+ case 0x10: // key update
+ {
+ int k=(buffer[index+1]>>6)+1; // key counter
+ if(nlen!=k*9) { badnano=true; break; }
+ for(i=0 ; i<k ; i++) {
+ id[i]= buffer[index+2+0+i*9];
+ pk[i]=&buffer[index+2+1+i*9];
+ numKeys++;
+ }
+ }
+ break;
+ case 0x00: // date
+ if(nlen<2) { badnano=true; break; }
+ date=WORD(buffer,index+2,0xFFFF);
+ break;
+ case 0x28: // pmk & provid update
+ if(nlen!=13) { badnano=true; break; }
+ prov= buffer[index+2+0];
+ mk= &buffer[index+2+2];
+ prvId[0]=buffer[index+2+10];
+ prvId[1]=buffer[index+2+11];
+ prvId[2]=buffer[index+2+12];
+ break;
+ case 0x29: // pmk update
+ if(nlen!=10) { badnano=true; break; }
+ prov= buffer[index+2+0];
+ mk= &buffer[index+2+2];
+ break;
+ case 0x11: // channel id
+ if(nlen!=6) { badnano=true; break; }
+ //chId[0]=buffer[index+2+0];
+ //chId[1]=buffer[index+2+1];
+ break;
+ case 0x91: // erase channel id
+ if(nlen!=6) { badnano=true; break; }
+ //eraseChId[0]=buffer[index+2+0];
+ //eraseChId[1]=buffer[index+2+1];
+ break;
+ case 0x8B: // CB20-matrix
+ if(nlen!=0x20) { badnano=true; break; }
+ //cb20ptr=&buffer[index+2];
+ break;
+ case 0x22: // set country code
+ if(nlen!=3) { badnano=true; break; }
+ //
+ break;
+ case 0x95: // unknown
+ if(nlen!=2) { badnano=true; break; }
+ //
+ break;
+ case 0x1E: // unknown
+ if(nlen!=15) { badnano=true; break; }
+ //
+ break;
+ case 0x1F: // unknown
+ if(nlen!=3) { badnano=true; break; }
+ //
+ break;
+ case 0x16: // unknown
+ if(nlen!=2) { badnano=true; break; }
+ //
+ break;
+ case 0x12: // unknown
+ if(nlen!=6) { badnano=true; break; }
+ //
+ break;
+
+ default:
+ PRINTF(L_SYS_EMM,"%d: unhandled nano 0x%02x",CardNum(),nano);
+ break;
+ }
+ index+=nlen+2;
+ }
+ if(badnano || index!=maxIndex) {
+ PRINTF(L_SYS_EMM,"%d: bad nano/bad paket",CardNum());
+ return;
+ }
+
+ // lastKey: save cpu time if we get bursts of the same key
+ if((numKeys>0 && (id[0]!=lastKey || numKeys>1)) || mk) {
+ memcpy(savebuf,buffer,n); // save the buffer
+ cIrdCardInfo *ci=Icards.First();
+ while(ci) {
+ ci->hexBase=cParseIrdeto::AddrBase(buffer);
+ if((numKeys>0 && (ci->cProviderIrdeto::MatchEMM(buffer) || CheckNull(ci->provId,sizeof(ci->provId)) )) ||
+ (mk && ci->haveHMK && (ci->cCardIrdeto::MatchEMM(buffer)))) {
+ LBSTARTF(L_SYS_EMM);
+ LBPUT("%02x %02x%02x%02x",buffer[3],buffer[4],buffer[5],buffer[6]);
+
+ for(i=0 ; i<numKeys ; i++) {
+ lastKey=id[i];
+ SessionKeyCrypt(pk[i],ci->PMK,date);
+ }
+ unsigned char chkkey[max(sizeof(ci->PMK),sizeof(ci->HMK))];
+ int keylen;
+ if(mk) {
+ memcpy(chkkey,ci->HMK,sizeof(ci->HMK)); // key is modified in decryptIrd()
+ DecryptIrd(mk,chkkey,128,16);
+ keylen=sizeof(ci->HMK);
+ memcpy(chkkey,ci->HMK,sizeof(ci->HMK)); // signature check with HMK
+ }
+ else {
+ keylen=sizeof(ci->PMK);
+ memcpy(chkkey,ci->PMK,sizeof(ci->PMK)); // signature check with PMK
+ }
+
+ memcpy(&buffer[sindex-6],adr,5);
+ if(SignatureCheck(&buffer[sindex-6],slen+6,chkkey,date,&buffer[sindex+slen],keylen))
+ {
+ char str[20];
+ LBPUT(" - OK");
+ for(i=0 ; i<numKeys ; i++) LBPUT(" - PK %02x %s",id[i],KeyStr(str,pk[i]));
+ if(mk) LBPUT(" - PMK %s",KeyStr(str,mk));
+
+ for(i=0 ; i<numKeys ; i++) {
+ FoundKey();
+ if(keys.NewKey('I',ci->provBase,id[i],pk[i],8)) NewKey();
+ }
+ if(mk) {
+ FoundKey();
+ if(Icards.Update(ci,mk,prvId[0]?prvId:0)) NewKey();
+ }
+ cLoaders::SaveCache();
+ break;
+ }
+ else {
+ LBPUT(" - FAIL");
+ }
+ LBEND();
+
+ memcpy(buffer,savebuf,n); // restore the buffer
+ }
+ ci=Icards.Next(ci);
+ }
+ }
+}
+
+// -- cSystemLinkIrd -----------------------------------------------------------
+
+class cSystemLinkIrd : public cSystemLink {
+public:
+ cSystemLinkIrd(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemIrd; }
+ virtual bool Init(const char *cfgdir);
+ };
+
+static cSystemLinkIrd staticInit;
+
+cSystemLinkIrd::cSystemLinkIrd(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkIrd::CanHandle(unsigned short SysId)
+{
+ SysId&=SYSTEM_MASK;
+ return SYSTEM_CAN_HANDLE(SysId);
+}
+
+bool cSystemLinkIrd::Init(const char *cfgdir)
+{
+ Icards.Load(cfgdir,SYSTEM_NAME,"Ird-Beta.KID");
+ return true;
+}
--- /dev/null
+#
+# Irdeto
+#
+TARGET = irdeto
+OBJS = irdeto.o
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "data.h"
+#include "cpu.h"
+#include "log-nagra.h"
+
+#define LOGLBPUT(t...) loglb->Printf(t)
+#define CCLOGLBPUT(t...) do { if(doDisAsm) loglb->Printf(t); } while(0)
+
+// -- cMapMem ------------------------------------------------------------------
+
+cMapMem::cMapMem(unsigned short Offset, int Size)
+{
+ offset=Offset; size=Size;
+ if((mem=(unsigned char *)malloc(size)))
+ memset(mem,0,size);
+ PRINTF(L_SYS_EMU,"mapmem: new map off=%04x size=%04x",offset,size);
+}
+
+cMapMem::~cMapMem()
+{
+ free(mem);
+}
+
+bool cMapMem::IsFine(void)
+{
+ return (mem!=0);
+}
+
+unsigned char cMapMem::Get(unsigned short ea)
+{
+ return (ea>=offset && ea<offset+size) ? mem[ea-offset] : 0;
+}
+
+void cMapMem::Set(unsigned short ea, unsigned char val)
+{
+ if(ea>=offset && ea<offset+size)
+ mem[ea-offset]=val;
+}
+
+// -- cMapRom ------------------------------------------------------------------
+
+cMapRom::cMapRom(unsigned short Offset, const char *Filename, int InFileOffset)
+{
+ offset=Offset; addr=0;
+ fm=filemaps.GetFileMap(Filename,FILEMAP_DOMAIN,false);
+ if(fm && fm->Map()) {
+ addr=fm->Addr()+InFileOffset;
+ size=fm->Size()-InFileOffset;
+ PRINTF(L_SYS_EMU,"maprom: new map off=%04x size=%04x",offset,size);
+ }
+}
+
+cMapRom::~cMapRom()
+{
+ if(fm) fm->Unmap();
+}
+
+bool cMapRom::IsFine(void)
+{
+ return (addr!=0);
+}
+
+unsigned char cMapRom::Get(unsigned short ea)
+{
+ return (ea>=offset && ea<offset+size) ? addr[ea-offset] : 0;
+}
+
+void cMapRom::Set(unsigned short ea, unsigned char val)
+{
+ if(ea>=offset && ea<offset+size) LOGLBPUT("[ROM] ");
+ // this is a ROM!
+}
+
+// -- cMapEeprom ---------------------------------------------------------------
+
+cMapEeprom::cMapEeprom(unsigned short Offset, const char *Filename, int OtpSize, int InFileOffset)
+{
+ offset=Offset; otpSize=OtpSize; addr=0;
+ fm=filemaps.GetFileMap(Filename,FILEMAP_DOMAIN,true);
+ if(fm && fm->Map()) {
+ addr=fm->Addr()+InFileOffset;
+ size=fm->Size()-InFileOffset;
+ PRINTF(L_SYS_EMU,"mapeeprom: new map off=%04x size=%04x otp=%04x",offset,size,otpSize);
+ }
+}
+
+cMapEeprom::~cMapEeprom()
+{
+ if(fm) fm->Unmap();
+}
+
+bool cMapEeprom::IsFine(void)
+{
+ return (addr!=0);
+}
+
+unsigned char cMapEeprom::Get(unsigned short ea)
+{
+ return (ea>=offset && ea<offset+size) ? addr[ea-offset] : 0;
+}
+
+void cMapEeprom::Set(unsigned short ea, unsigned char val)
+{
+ if(ea>=offset && ea<offset+otpSize) {
+ if(addr[ea-offset]==0) {
+ addr[ea-offset]=val;
+ LOGLBPUT("[OTP-SET] ");
+ }
+ LOGLBPUT("[OTP] ");
+ }
+ if(ea>=offset+otpSize && ea<offset+size) {
+ addr[ea-offset]=val;
+ LOGLBPUT("[EEP] ");
+ }
+}
+
+// -- c6805 --------------------------------------------------------------------
+
+#define HILO(ea) ((Get(ea)<<8)+Get((ea)+1))
+#define HILOS(s,ea) ((Get((s),(ea))<<8)+Get((s),(ea)+1))
+#define PAGEOFF(ea,s) (((ea)&0x8000) ? pageMap[s]:0)
+
+c6805::c6805(void) {
+ cc.c=0; cc.z=0; cc.n=0; cc.i=0; cc.h=0; cc.v=1;
+ pc=0; a=0; x=0; y=0; cr=dr=0; sp=spHi=0x100; spLow=0xC0;
+ ClearBreakpoints();
+ InitMapper();
+ memset(stats,0,sizeof(stats));
+ loglb=new cLineBuff(128);
+}
+
+c6805::~c6805()
+{
+ if(LOG(L_SYS_CPUSTATS)) {
+ int i, j, sort[256];
+ for(i=0; i<256; i++) sort[i]=i;
+ for(i=0; i<256; i++)
+ for(j=0; j<255; j++)
+ if(stats[sort[j]]<stats[sort[j+1]]) {
+ int x=sort[j]; sort[j]=sort[j+1]; sort[j+1]=x;
+ }
+ PRINTF(L_SYS_CPUSTATS,"opcode statistics");
+ for(i=0; i<256; i++)
+ if((j=stats[sort[i]])) PRINTF(L_SYS_CPUSTATS,"opcode %02x: %d",sort[i],j);
+ }
+ ClearMapper();
+ delete loglb;
+}
+
+bool c6805::AddMapper(cMap *map, unsigned short start, int size, unsigned char seg)
+{
+ if(map && map->IsFine()) {
+ if(nextMapper<MAX_MAPPER) {
+ map->loglb=loglb;
+ mapper[nextMapper]=map;
+ memset(&mapMap[start+PAGEOFF(start,seg)],nextMapper,size);
+ nextMapper++;
+ return true;
+ }
+ else PRINTF(L_SYS_EMU,"6805: too many mappers");
+ }
+ else PRINTF(L_SYS_EMU,"6805: mapper not ready");
+ delete map;
+ return false;
+}
+
+void c6805::ResetMapper(void)
+{
+ ClearMapper(); InitMapper();
+}
+
+void c6805::InitMapper(void)
+{
+ memset(mapMap,0,sizeof(mapMap));
+ memset(mapper,0,sizeof(mapper));
+ mapper[0]=new cMapMem(0,PAGE_SIZE*2);
+ nextMapper=1;
+ memset(pageMap,0,sizeof(pageMap));
+ pageMap[0]=PAGE_SIZE*0;
+ pageMap[1]=PAGE_SIZE*1;
+ pageMap[2]=PAGE_SIZE*2;
+ pageMap[0x80]=PAGE_SIZE*3;
+}
+
+void c6805::ClearMapper(void)
+{
+ for(int i=0; i<MAX_MAPPER; i++) delete mapper[i];
+}
+
+unsigned char c6805::Get(unsigned short ea) const
+{
+ return mapper[mapMap[ea+PAGEOFF(ea,cr)]&0x7f]->Get(ea);
+}
+
+unsigned char c6805::Get(unsigned char seg, unsigned short ea) const
+{
+ return mapper[mapMap[ea+PAGEOFF(ea,seg)]&0x7f]->Get(ea);
+}
+
+void c6805::Set(unsigned short ea, unsigned char val)
+{
+ unsigned char mapId=mapMap[ea+PAGEOFF(ea,cr)];
+ if(!(mapId&0x80)) mapper[mapId&0x7f]->Set(ea,val);
+}
+
+void c6805::Set(unsigned char seg, unsigned short ea, unsigned char val)
+{
+ unsigned char mapId=mapMap[ea+PAGEOFF(ea,seg)];
+ if(!(mapId&0x80)) mapper[mapId&0x7f]->Set(ea,val);
+}
+
+void c6805::ForceSet(unsigned short ea, unsigned char val, bool ro)
+{
+ mapMap[ea]=0; // reset to RAM map
+ Set(0,ea,val); // set value
+ if(ro) mapMap[ea]|=0x80; // protect byte
+}
+
+void c6805::SetMem(unsigned short addr, const unsigned char *data, int len, unsigned char seg)
+{
+ while(len>0) { Set(seg,addr++,*data++); len--; }
+}
+
+void c6805::GetMem(unsigned short addr, unsigned char *data, int len, unsigned char seg) const
+{
+ while(len>0) { *data++=Get(seg,addr++); len--; }
+}
+
+void c6805::SetSp(unsigned short SpHi, unsigned short SpLow)
+{
+ spHi =sp=SpHi;
+ spLow =SpLow;
+}
+
+void c6805::SetPc(unsigned short addr)
+{
+ pc=addr;
+}
+
+void c6805::PopPc(void)
+{
+ poppc();
+}
+
+void c6805::PopCr(void)
+{
+ cr=pop();
+}
+
+void c6805::AddBreakpoint(unsigned short addr)
+{
+ if(numBp<MAX_BREAKPOINTS) {
+ bp[numBp++]=addr;
+ PRINTF(L_SYS_EMU,"6805: setting breakpoint at 0x%04x",addr);
+ }
+ else PRINTF(L_SYS_EMU,"6805: too many breakpoints");
+}
+
+void c6805::ClearBreakpoints(void)
+{
+ numBp=0;
+ memset(bp,0,sizeof(bp));
+}
+
+static const char * const ops[] = {
+// 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0a 0x0b 0x0c 0x0d 0x0e 0x0f
+/* 0x00 */ "BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR","BRSET","BRCLR",
+/* 0x10 */ "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR", "BSET", "BCLR",
+/* 0x20 */ "BRA", "BRN", "BHI", "BLS", "BCC", "BCS", "BNE", "BEQ", "BHCC", "BHCS", "BPL", "BMI", "BMC", "BMS", "BIL", "BIH",
+/* 0x30 */ "NEG", "pre31","pre32","COM", "LSR", "op35", "ROR", "ASR", "ASL", "ROL", "DEC", "op3b", "INC", "TST", "SWAP", "CLR",
+/* 0x40 */ "NEG", "op41", "MUL", "COM", "LSR", "op45", "ROR", "ASR", "ASL", "ROL", "DEC", "op4b", "INC", "TST", "SWAP", "CLR",
+/* 0x50 */ "NEG", "op51", "MUL", "COM", "LSR", "op55", "ROR", "ASR", "ASL", "ROL", "DEC", "op5b", "INC", "TST", "SWAP", "CLR",
+/* 0x60 */ "NEG", "op61", "op62", "COM", "LSR", "op65", "ROR", "ASR", "ASL", "ROL", "DEC", "op6b", "INC", "TST", "SWAP", "CLR",
+/* 0x70 */ "NEG", "LDD", "LDD", "COM", "LSR", "LDD", "ROR", "ASR", "ASL", "ROL", "DEC", "TAD", "INC", "TST", "SWAP", "CLR",
+/* 0x80 */ "RTI", "RTS", "op82", "SWI", "POPA", "POP%c","POPC", "PRTS", "PUSHA","PUSH%c","PUSHC","TDA", "TCA", "PJSR", "STOP", "WAIT",
+/* 0x90 */ "pre90","pre91","pre92","T%2$c%1$c","T%cS","TAS","TS%c","TA%c", "CLC", "SEC", "CLI", "SEI", "RSP", "NOP", "TSA", "T%cA",
+/* 0xa0 */ "SUB", "CMP", "SBC", "CP%c", "AND", "BIT", "LDA", "opa7", "EOR", "ADC", "ORA", "ADD", "PUSHD","BSR", "LD%c", "POPD",
+/* 0xb0 */ "SUB", "CMP", "SBC", "CP%c", "AND", "BIT", "LDA", "STA", "EOR", "ADC", "ORA", "ADD", "JMP", "JSR", "LD%c", "ST%c",
+/* 0xc0 */ "SUB", "CMP", "SBC", "CP%c", "AND", "BIT", "LDA", "STA", "EOR", "ADC", "ORA", "ADD", "JMP", "JSR", "LD%c", "ST%c",
+/* 0xd0 */ "SUB", "CMP", "SBC", "CP%c", "AND", "BIT", "LDA", "STA", "EOR", "ADC", "ORA", "ADD", "JMP", "JSR", "LD%c", "ST%c",
+/* 0xe0 */ "SUB", "CMP", "SBC", "CP%c", "AND", "BIT", "LDA", "STA", "EOR", "ADC", "ORA", "ADD", "JMP", "JSR", "LD%c", "ST%c",
+/* 0xf0 */ "SUB", "CMP", "SBC", "CP%c", "AND", "BIT", "LDA", "STA", "EOR", "ADC", "ORA", "ADD", "JMP", "JSR", "LD%c", "ST%c",
+ };
+static const char * const vops[] = {
+ "BVGT","BVLE","BVGE","BVLT","BVC","BVS"
+ };
+
+// Flags:
+// 1 - read operant
+// 2 - write operant
+// 4 - use dr register
+// 8 - special address mode in high nibble
+static const char opFlags[] = {
+// 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f
+/* 0x00 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+/* 0x10 */ 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03,
+/* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x30 */ 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+/* 0x40 */ 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+/* 0x50 */ 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+/* 0x60 */ 0x03, 0x00, 0x00, 0x03, 0x03, 0x00, 0x03, 0x03, 0x03, 0x03, 0x03, 0x00, 0x03, 0x01, 0x03, 0x02,
+/* 0x70 */ 0x03, 0xA9, 0xB9, 0x03, 0x03, 0xC9, 0x03, 0x03, 0x03, 0x03, 0x03, 0x88, 0x03, 0x01, 0x03, 0x02,
+/* 0x80 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0x90 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+/* 0xa0 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x01, 0x01, 0x01, 0x01, 0x88, 0x88, 0x01, 0x88,
+/* 0xb0 */ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x02,
+/* 0xc0 */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x05, 0x06,
+/* 0xd0 */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x05, 0x06,
+/* 0xe0 */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x05, 0x06,
+/* 0xf0 */ 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x05, 0x05, 0x05, 0x05, 0x00, 0x00, 0x05, 0x06,
+ };
+
+char * c6805::PADDR(unsigned char s, unsigned short ea)
+{
+ snprintf(addrBuff,sizeof(addrBuff),((ea&0x8000) && s>0)?"%2$x:%1$04x":"%04x",ea,s);
+ return addrBuff;
+}
+
+int c6805::Run(int max_count)
+{
+// returns:
+// 0 - breakpoint
+// 1 - stack overflow
+// 2 - instruction counter exeeded
+// 3 - unsupported instruction
+
+ disAsmLogClass=L_SYS_DISASM;
+ if(LOG(L_SYS_DISASM)) doDisAsm=true;
+ else {
+ doDisAsm=false;
+ if(LOG(L_SYS_DISASM80)) disAsmLogClass=L_SYS_DISASM80;
+ }
+ PRINTF(disAsmLogClass,"cr:-pc- aa xx yy dr -sp- VHINZC -mem@pc- -mem@sp-");
+
+ int count=0;
+ while (1) {
+ if(sp<spLow) {
+ PRINTF(L_SYS_EMU,"stack overflow (count=%d)",count);
+ return 1;
+ }
+ if(spHi>spLow && sp>spHi) {
+ PRINTF(L_SYS_EMU,"stack underflow (count=%d)",count);
+ return 1;
+ }
+ count++;
+
+ if(!LOG(L_SYS_DISASM) && LOG(L_SYS_DISASM80)) {
+ bool flag=(pc>=0x80 && pc<0xE0);
+ if(doDisAsm && !flag) PRINTF(disAsmLogClass,"[...]");
+ doDisAsm=flag;
+ }
+
+ CCLOGLBPUT("%02x:%04x %02x %02x %02x %02x %04x %c%c%c%c%c%c %02x%02x%02x%02x %02x%02x%02x%02x ",
+ cr,pc,a,x,y,dr,sp,
+ cc.v?'V':'.',cc.h?'H':'.',cc.i?'I':'.',cc.n?'N':'.',cc.z?'Z':'.',cc.c?'C':'.',
+ Get(pc),Get(pc+1),Get(pc+2),Get(pc+3),Get(sp+1),Get(sp+2),Get(sp+3),Get(sp+4));
+
+ Stepper();
+ unsigned char *ex=&x;
+ unsigned short idx=*ex;
+ indirect=false;
+ bool paged=false, vbra=false;
+ unsigned char ins=Get(pc++);
+ char xs='X', xi='X';
+
+ // check pre-bytes
+ switch(ins) {
+ case 0x31: // use SP indexed or indirect paged mode (ST19)
+ ins=Get(pc++);
+ switch(ins) {
+ case 0x22 ... 0x27: vbra=true; PRINTF(L_SYS_EMU,"WARN: V-flag not yet calculated"); break;
+ case 0x75:
+ case 0x8D:
+ case 0xC0 ... 0xCB:
+ case 0xCE ... 0xCF:
+ case 0xD0 ... 0xDB:
+ case 0xDE ... 0xDF: paged=true; indirect=true; break;
+ case 0xE0 ... 0xEB:
+ case 0xEE ... 0xEF: idx=sp; xi='S'; break;
+ }
+ break;
+ case 0x32: // use indirect SP indexed or indirect paged Y indexed mode (ST19)
+ ins=Get(pc++);
+ switch(ins) {
+ case 0x22 ... 0x27: vbra=true; PRINTF(L_SYS_EMU,"WARN: V-flag not yet calculated"); indirect=true; break;
+ case 0xC3:
+ case 0xCE ... 0xCF:
+ case 0xD0 ... 0xDB:
+ case 0xDE ... 0xDF: paged=true; indirect=true; ex=&y; idx=*ex; xs='Y'; xi='Y'; break;
+ case 0xE0 ... 0xEB:
+ case 0xEE ... 0xEF: indirect=true; idx=sp; xi='S'; break;
+ }
+ break;
+ case 0x91: // use Y register with indirect addr mode (ST7)
+ indirect=true;
+ // fall through
+ case 0x90: // use Y register (ST7)
+ ex=&y; idx=*ex; xs='Y'; xi='Y';
+ ins=Get(pc++);
+ break;
+ case 0x92: // use indirect addr mode (ST7)
+ indirect=true;
+ ins=Get(pc++);
+ break;
+ }
+
+ if(doDisAsm) {
+ char str[8];
+ if(!vbra) snprintf(str,sizeof(str),ops[ins],xs,xs^1);
+ else snprintf(str,sizeof(str),"%s",vops[ins-0x22]);
+ LOGLBPUT("%-5s ",str);
+ }
+
+ // address decoding
+ unsigned short ea=0;
+ unsigned char flags=opFlags[ins];
+ unsigned char pr=(flags&4) ? dr:cr;
+ switch(((flags&8) ? flags:ins)>>4) {
+ case 0x2: // no or special address mode
+ case 0x8:
+ case 0x9:
+ break;
+ case 0xA: // immediate
+ CCLOGLBPUT("#%02x ",Get(pc));
+ ea=pc++; break;
+ case 0x3: // short
+ case 0xB:
+ ea=Get(pc++);
+ if(!indirect) { // short direct
+ }
+ else { // short indirect
+ CCLOGLBPUT("[%02x] -> ",ea);
+ ea=Get(ea);
+ }
+ CCLOGLBPUT("%02x ",ea);
+ break;
+ case 0xC: // long
+ if(!indirect) { // long direct
+ ea=HILO(pc); pc+=2;
+ }
+ else { // long indirect
+ if(paged) {
+ ea=HILO(pc); pc+=2;
+ CCLOGLBPUT("[%s] -> ",PADDR(pr,ea));
+ unsigned char s=Get(pr,ea);
+ ea=HILOS(pr,ea+1);
+ pr=s;
+ }
+ else {
+ ea=Get(pc++);
+ CCLOGLBPUT("[%02x] -> ",ea);
+ ea=HILO(ea);
+ }
+ }
+ CCLOGLBPUT("%s ",PADDR(pr,ea));
+ break;
+ case 0xD: // long indexed
+ if(!indirect) { // long direct indexed
+ ea=HILO(pc); pc+=2;
+ CCLOGLBPUT("(%s",PADDR(cr,ea));
+ }
+ else { // long indirect indexed
+ if(paged) {
+ ea=HILO(pc); pc+=2;
+ CCLOGLBPUT("([%s]",PADDR(pr,ea));
+ unsigned char s=Get(pr,ea++);
+ ea=HILOS(pr,ea);
+ pr=s;
+ }
+ else {
+ ea=Get(pc++);
+ CCLOGLBPUT("([%02x]",ea);
+ ea=HILO(ea);
+ }
+ CCLOGLBPUT(",%c) -> (%s",xi,PADDR(pr,ea));
+ }
+ ea+=idx;
+ CCLOGLBPUT(",%c) -> %s ",xi,PADDR(pr,ea));
+ break;
+ case 0x6: // short indexed
+ case 0xE:
+ ea=Get(pc++);
+ if(!indirect) { // short direct indexed
+ CCLOGLBPUT("(%02x",ea);
+ }
+ else { // short indirect indexed
+ CCLOGLBPUT("([%02x]",ea);
+ ea=Get(ea);
+ CCLOGLBPUT(",%c) -> (%02x",xi,ea);
+ }
+ ea+=idx;
+ CCLOGLBPUT(",%c) -> %s ",xi,PADDR(pr,ea));
+ break;
+ case 0x7: // indexed
+ case 0xF:
+ ea=idx;
+ CCLOGLBPUT("(%c) -> %s ",xi,PADDR(pr,ea));
+ break;
+ case 0x4: // inherent A
+ CCLOGLBPUT("A ");
+ break;
+ case 0x5: // inherent X/Y
+ CCLOGLBPUT("%c ",xs);
+ break;
+ case 0x0: // bit
+ case 0x1:
+ ea=Get(pc++);
+ if(!indirect) {
+ }
+ else {
+ CCLOGLBPUT("[%02x] -> ",ea);
+ ea=Get(ea);
+ indirect=false; // don't use indirect mode in case this is a bit branch
+ }
+ CCLOGLBPUT("%02x ",ea);
+ break;
+ }
+
+ // read operant
+ unsigned char op=0;
+ if(flags & 1) {
+ switch(((flags&8) ? flags:ins)>>4) {
+ case 0x2: // no or special address mode
+ case 0x8:
+ case 0x9:
+ break;
+ case 0x3: // short
+ case 0xB:
+ case 0xC: // long
+ case 0xD: // long indexed
+ case 0x6: // short indexed
+ case 0xE:
+ case 0x7: // indexed
+ case 0xF:
+ case 0x0: // bit
+ case 0x1:
+ op=Get(pr,ea);
+ CCLOGLBPUT("{%02x} ",op);
+ break;
+ case 0xA: // immediate
+ op=Get(pr,ea);
+ break;
+ case 0x4: // inherent A
+ op=a; break;
+ case 0x5: // inherent X/Y
+ op=*ex; break;
+ }
+ }
+
+ // command decoding
+ stats[ins]++;
+ switch(ins) {
+ case 0xA6: // LDA
+ case 0xB6:
+ case 0xC6:
+ case 0xD6:
+ case 0xE6:
+ case 0xF6:
+ a=op; tst(op); break;
+ case 0xAE: // LDX
+ case 0xBE:
+ case 0xCE:
+ case 0xDE:
+ case 0xEE:
+ case 0xFE:
+ *ex=op; tst(op); break;
+ case 0xB7: // STA
+ case 0xC7:
+ case 0xD7:
+ case 0xE7:
+ case 0xF7:
+ op=a; tst(op); break;
+ case 0xBF: // STX
+ case 0xCF:
+ case 0xDF:
+ case 0xEF:
+ case 0xFF:
+ op=*ex; tst(op); break;
+ case 0x97: // TAX
+ *ex=a; break;
+ case 0x9F: // TXA
+ a=*ex; break;
+ case 0x93: // TYX (ST7)
+ if(ex==&x) *ex=y; else *ex=x; break;
+ case 0x3D: // TST
+ case 0x4D:
+ case 0x5D:
+ case 0x6D:
+ case 0x7D:
+ tst(op); break;
+ case 0x3F: // CLR
+ case 0x4F:
+ case 0x5F:
+ case 0x6F:
+ case 0x7F:
+ op=0; tst(0); break;
+ case 0x3C: // INC
+ case 0x4C:
+ case 0x5C:
+ case 0x6C:
+ case 0x7C:
+ op++; tst(op); break;
+ case 0x3A: // DEC
+ case 0x4A:
+ case 0x5A:
+ case 0x6A:
+ case 0x7A:
+ op--; tst(op); break;
+ case 0x33: // COM
+ case 0x43:
+ case 0x53:
+ case 0x63:
+ case 0x73:
+ op=~op; cc.c=1; tst(op); break;
+ case 0x30: // NEG
+ case 0x40:
+ case 0x50:
+ case 0x60:
+ case 0x70:
+ op=~op+1; if(!op) cc.c=0; tst(op); break;
+ case 0x42: // MUL
+ case 0x52:
+ {
+ unsigned short res=*ex * a;
+ *ex=(res>>8); a=res&0xff; cc.c=0; cc.h=0;
+ break;
+ }
+ case 0xA9: // ADC
+ case 0xB9:
+ case 0xC9:
+ case 0xD9:
+ case 0xE9:
+ case 0xF9:
+ a=add(op,cc.c); break;
+ case 0xAB: // ADD
+ case 0xBB:
+ case 0xCB:
+ case 0xDB:
+ case 0xEB:
+ case 0xFB:
+ a=add(op,0); break;
+ case 0xA2: // SBC
+ case 0xB2:
+ case 0xC2:
+ case 0xD2:
+ case 0xE2:
+ case 0xF2:
+ a=sub(a,op,cc.c); break;
+ case 0xA0: // SUB
+ case 0xB0:
+ case 0xC0:
+ case 0xD0:
+ case 0xE0:
+ case 0xF0:
+ a=sub(a,op,0); break;
+ case 0xA1: // CMP
+ case 0xB1:
+ case 0xC1:
+ case 0xD1:
+ case 0xE1:
+ case 0xF1:
+ sub(a,op,0); break;
+ case 0xA3: // CPX
+ case 0xB3:
+ case 0xC3:
+ case 0xD3:
+ case 0xE3:
+ case 0xF3:
+ sub(*ex,op,0); break;
+ case 0xA4: // AND
+ case 0xB4:
+ case 0xC4:
+ case 0xD4:
+ case 0xE4:
+ case 0xF4:
+ a &= op; tst(a); break;
+ case 0xAA: // ORA
+ case 0xBA:
+ case 0xCA:
+ case 0xDA:
+ case 0xEA:
+ case 0xFA:
+ a |= op; tst(a); break;
+ case 0xA8: // EOR
+ case 0xB8:
+ case 0xC8:
+ case 0xD8:
+ case 0xE8:
+ case 0xF8:
+ a ^= op; tst(a); break;
+ case 0xA5: // BIT
+ case 0xB5:
+ case 0xC5:
+ case 0xD5:
+ case 0xE5:
+ case 0xF5:
+ tst(a & op); break;
+ case 0x38: // ASL
+ case 0x48:
+ case 0x58:
+ case 0x68:
+ case 0x78:
+ op=rollL(op,0); break;
+ case 0x39: // ROL
+ case 0x49:
+ case 0x59:
+ case 0x69:
+ case 0x79:
+ op=rollL(op,cc.c); break;
+ case 0x37: // ASR
+ case 0x47:
+ case 0x57:
+ case 0x67:
+ case 0x77:
+ op=rollR(op,bitset(op,7)); break;
+ case 0x34: // LSR
+ case 0x44:
+ case 0x54:
+ case 0x64:
+ case 0x74:
+ op=rollR(op,0); break;
+ case 0x36: // ROR
+ case 0x46:
+ case 0x56:
+ case 0x66:
+ case 0x76:
+ op=rollR(op,cc.c); break;
+ case 0x3E: // SWAP (ST7)
+ case 0x4E:
+ case 0x5E:
+ case 0x6E:
+ case 0x7E:
+ op=(op<<4)|(op>>4); tst(op); break;
+ case 0x00 ... 0x0F: // BRSET BRCLR
+ {
+ int bit=(ins&0x0F)>>1;
+ CCLOGLBPUT(",#%x,",bit);
+ cc.c=bitset(op,bit);
+ branch((ins&0x01) ? !cc.c:cc.c);
+ break;
+ }
+ case 0x10 ... 0x1F: // BSET BCLR
+ {
+ int bit=(ins&0x0F)>>1;
+ CCLOGLBPUT(",#%x",bit);
+ if(ins&0x01) op &= ~(1<<bit);
+ else op |= (1<<bit);
+ break;
+ }
+ case 0x20: // BRA
+ branch(true); break;
+ case 0x21: // BRN
+ branch(false); break;
+ case 0x22: // BHI
+ if(vbra) branch(!cc.z && ((cc.n && cc.v) || (!cc.n && !cc.v)));
+ else branch(!cc.c && !cc.z);
+ break;
+ case 0x23: // BLS
+ if(vbra) branch(cc.z || (cc.n && !cc.v) || (!cc.n && cc.v));
+ else branch( cc.c || cc.z);
+ break;
+ case 0x24: // BCC BHS
+ if(vbra) branch((cc.n && cc.v) || (!cc.n && !cc.v));
+ else branch(!cc.c);
+ break;
+ case 0x25: // BCS BLO
+ if(vbra) branch((cc.n && !cc.v) || (!cc.n && cc.v));
+ else branch( cc.c);
+ break;
+ case 0x26: // BNE
+ if(vbra) branch(!cc.v);
+ else branch(!cc.z);
+ break;
+ case 0x27: // BEQ
+ if(vbra) branch(cc.v);
+ else branch( cc.z); break;
+ case 0x28: // BHCC
+ branch(!cc.h); break;
+ case 0x29: // BHCS
+ branch( cc.h); break;
+ case 0x2A: // BPL
+ branch(!cc.n); break;
+ case 0x2B: // BMI
+ branch( cc.n); break;
+ case 0x2C: // BMC
+ branch(!cc.i); break;
+ case 0x2D: // BMS
+ branch( cc.i); break;
+ case 0xBC: // JMP
+ case 0xCC:
+ case 0xDC:
+ case 0xEC:
+ case 0xFC:
+ pc=ea; break;
+ case 0xAD: // BSR
+ pc++; pushpc(); pc--; branch(true); break;
+ case 0xBD: // JSR
+ case 0xCD:
+ case 0xDD:
+ case 0xED:
+ case 0xFD:
+ pushpc(); pc=ea; break;
+ case 0x81: // RTS
+ poppc(); break;
+ case 0x83: // SWI
+ pushpc(); push(x); push(a); pushc();
+ cc.i=1; pc=HILO(0x1ffc); break;
+ case 0x80: // RTI
+ popc(); a=pop(); x=pop(); poppc(); break;
+ case 0x9C: // RSP
+ sp=spHi; break;
+ case 0x96: // TSX
+ *ex=sp; break;
+ case 0x94: // TXS (ST7)
+ sp=*ex; break;
+ case 0x9E: // TSA
+ a=sp; break;
+ case 0x95: // TAS (ST7)
+ sp=a; break;
+ case 0x84: // POPA (ST7)
+ a=pop(); break;
+ case 0x85: // POPX (ST7)
+ *ex=pop(); break;
+ case 0x86: // POPC (ST7)
+ popc(); break;
+ case 0x88: // PUSHA (ST7)
+ push(a); break;
+ case 0x89: // PUSHX (ST7)
+ push(*ex); break;
+ case 0x8A: // PUSHC (ST7)
+ pushc(); break;
+ case 0x98: // CLC
+ cc.c=0; break;
+ case 0x99: // SEC
+ cc.c=1; break;
+ case 0x9A: // CLI
+ cc.i=0; break;
+ case 0x9B: // SEI
+ cc.i=1; break;
+ case 0x9D: // NOP
+ break;
+
+ case 0x71: // LDD (ST19)
+ case 0x72:
+ case 0x75:
+ dr=op; break;
+ case 0x7B: // TAD (ST19)
+ dr=a; break;
+ case 0x8B: // TDA (ST19)
+ a=dr; break;
+ case 0x8C: // TCA (ST19)
+ a=cr; break;
+ case 0xAC: // PUSHD (ST19)
+ push(dr); break;
+ case 0xAF: // POPD (ST19)
+ dr=pop(); break;
+ case 0x87: // PRTS (ST19)
+ cr=pop(); poppc(); break;
+ case 0x8D: // PJSR (ST19)
+ if(paged) {
+ ea=HILO(pc); pc+=2;
+ CCLOGLBPUT("[%s] -> ",PADDR(cr,ea));
+ }
+ else {
+ ea=pc; pc+=3;
+ }
+ pr=Get(ea++);
+ ea=HILO(ea);
+ CCLOGLBPUT("%s ",PADDR(pr,ea));
+ pushpc(); push(cr); cr=pr; pc=ea; break;
+
+ case 0x90: // pre-bytes
+ case 0x91:
+ case 0x92:
+ case 0x31:
+ case 0x32:
+ PRINTF(L_SYS_EMU,"pre-byte %02x in command decoding (count=%d)",ins,count);
+ return 3;
+ default:
+ PRINTF(L_SYS_EMU,"unsupported instruction 0x%02x (count=%d)",ins,count);
+ return 3;
+ }
+
+ // write operant
+ if(flags & 2) {
+ switch(((flags&8) ? flags:ins)>>4) {
+ case 0x2: // no or special address mode
+ case 0x8:
+ case 0x9:
+ break;
+ case 0xA: // immediate
+ case 0x3: // short
+ case 0xB:
+ case 0xC: // long
+ case 0xD: // long indexed
+ case 0x6: // short indexed
+ case 0xE:
+ case 0x7: // indexed
+ case 0xF:
+ case 0x0: // bit
+ case 0x1:
+ Set(ea,op); break;
+ case 0x4: // inherent A
+ a=op; break;
+ case 0x5: // inherent X/Y
+ *ex=op; break;
+ }
+ }
+ PUTLB(disAsmLogClass,loglb);
+
+ for(int i=numBp-1 ; i>=0 ; i--) {
+ if(bp[i]==pc) {
+ PRINTF(L_SYS_EMU,"6805: breakpoint at %04x (count=%d)",pc,count);
+ return 0;
+ }
+ }
+ if(count>=max_count) {
+ PRINTF(L_SYS_EMU,"max. instruction counter exceeded (count=%d)",count);
+ return 2;
+ }
+ }
+}
+
+void c6805::branch(bool branch)
+{
+ if(doDisAsm) {
+ unsigned char off=Get(pc);
+ if(indirect) {
+ LOGLBPUT("[%02x] -> ",off);
+ off=Get(off);
+ }
+ unsigned short npc=pc+off+1;
+ if(off&0x80) npc-=0x100; // gcc fixup. take care of sign
+ LOGLBPUT("%s ",PADDR(cr,npc));
+ if(branch) LOGLBPUT("(taken) ");
+ }
+ pc++;
+ if(branch) {
+ unsigned char offset=Get(pc-1);
+ if(indirect) offset=Get(offset);
+ pc+=offset;
+ if(offset&0x80) pc-=0x100; // gcc fixup. take care of sign
+ }
+}
+
+void c6805::push(unsigned char c)
+{
+ Set(sp--,c);
+}
+
+unsigned char c6805::pop(void)
+{
+ return Get(++sp);
+}
+
+void c6805::pushpc(void)
+{
+ push(pc & 0xff);
+ push(pc >> 8);
+}
+
+void c6805::poppc(void)
+{
+ pc=(pop()<<8) | pop();
+}
+
+void c6805::pushc(void)
+{
+ unsigned char c=0xC0+(cc.v?32:0)+(cc.h?16:0)+(cc.i?8:0)+(cc.n?4:0)+(cc.z?2:0)+(cc.c?1:0);
+ push(c);
+}
+
+void c6805::popc(void)
+{
+ unsigned char c=pop();
+ cc.v=(c&32) ? 1:0;
+ cc.h=(c&16) ? 1:0;
+ cc.i=(c& 8) ? 1:0;
+ cc.n=(c& 4) ? 1:0;
+ cc.z=(c& 2) ? 1:0;
+ cc.c=(c& 1) ? 1:0;
+}
+
+void c6805::tst(unsigned char c)
+{
+ cc.z=!c;
+ cc.n=bitset(c,7);
+}
+
+unsigned char c6805::add(unsigned char op, unsigned char c)
+{
+ unsigned short res_half=(a&0x0f) + (op&0x0f) + c;
+ unsigned short res=(unsigned short)a + (unsigned short)op + (unsigned short)c;
+ cc.h=res_half > 0x0f;
+ cc.c=res > 0xff;
+ res&=0xff;
+ tst(res);
+ return res;
+}
+
+unsigned char c6805::sub(unsigned char op1, unsigned char op2, unsigned char c)
+{
+ short res=(short)op1 - (short)op2 - (short)c;
+ cc.c=res < 0;
+ res&=0xff;
+ tst(res);
+ return res;
+}
+
+unsigned char c6805::rollR(unsigned char op, unsigned char c)
+{
+ cc.c=bitset(op,0);
+ op >>= 1;
+ op |= c << 7;
+ tst(op);
+ return op;
+}
+
+unsigned char c6805::rollL(unsigned char op, unsigned char c)
+{
+ cc.c=bitset(op,7);
+ op <<= 1;
+ op |= c;
+ tst(op);
+ return op;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __NAGRA_CPU_H
+#define __NAGRA_CPU_H
+
+#define FILEMAP_DOMAIN "nagra"
+
+class cFileMap;
+class cLineBuff;
+class c6805;
+
+// ----------------------------------------------------------------
+
+class cMap {
+friend class c6805;
+protected:
+ cLineBuff *loglb;
+public:
+ virtual ~cMap() {};
+ virtual unsigned char Get(unsigned short ea)=0;
+ virtual void Set(unsigned short ea, unsigned char val)=0;
+ virtual bool IsFine(void)=0;
+};
+
+// ----------------------------------------------------------------
+
+class cMapMem : public cMap {
+private:
+ unsigned short offset;
+ unsigned char *mem;
+ int size;
+public:
+ cMapMem(unsigned short Offset, int Size);
+ virtual ~cMapMem();
+ virtual unsigned char Get(unsigned short ea);
+ virtual void Set(unsigned short ea, unsigned char val);
+ virtual bool IsFine(void);
+ };
+
+// ----------------------------------------------------------------
+
+class cMapRom : public cMap {
+private:
+ unsigned short offset;
+ cFileMap *fm;
+ unsigned char *addr;
+ int size;
+public:
+ cMapRom(unsigned short Offset, const char *Filename, int InFileOffset=0);
+ virtual ~cMapRom();
+ virtual unsigned char Get(unsigned short ea);
+ virtual void Set(unsigned short ea, unsigned char val);
+ virtual bool IsFine(void);
+ };
+
+// ----------------------------------------------------------------
+
+class cMapEeprom : public cMap {
+private:
+ unsigned short offset;
+ cFileMap *fm;
+ unsigned char *addr;
+ int size, otpSize;
+public:
+ cMapEeprom(unsigned short Offset, const char *Filename, int OtpSize, int InFileOffset=0);
+ virtual ~cMapEeprom();
+ virtual unsigned char Get(unsigned short ea);
+ virtual void Set(unsigned short ea, unsigned char val);
+ virtual bool IsFine(void);
+ };
+
+// ----------------------------------------------------------------
+
+#define MAX_BREAKPOINTS 4
+#define MAX_MAPPER 8
+#define MAX_PAGES 4
+#define PAGE_SIZE 32*1024
+
+#define bitset(d,bit) (((d)>>(bit))&1)
+
+class c6805 {
+private:
+ unsigned short pc, sp, spHi, spLow;
+ unsigned short bp[MAX_BREAKPOINTS], numBp;
+ unsigned char mapMap[(MAX_PAGES+1)*PAGE_SIZE];
+ cMap *mapper[MAX_MAPPER];
+ int nextMapper;
+ int pageMap[256];
+ bool indirect;
+ //
+ void InitMapper(void);
+ void ClearMapper(void);
+ void branch(bool branch);
+ inline void tst(unsigned char c);
+ inline void push(unsigned char c);
+ inline unsigned char pop(void);
+ void pushpc(void);
+ void poppc(void);
+ void pushc(void);
+ void popc(void);
+ unsigned char add(unsigned char op, unsigned char c);
+ unsigned char sub(unsigned char op1, unsigned char op2, unsigned char c);
+ unsigned char rollR(unsigned char op, unsigned char c);
+ unsigned char rollL(unsigned char op, unsigned char c);
+protected:
+ unsigned char a, x, y, cr, dr;
+ struct CC { unsigned char c, z, n, i, h, v; } cc;
+ //
+ int Run(int max_count);
+ void AddBreakpoint(unsigned short addr);
+ void ClearBreakpoints(void);
+ bool AddMapper(cMap *map, unsigned short start, int size, unsigned char seg=0);
+ void ResetMapper(void);
+ unsigned char Get(unsigned short ea) const;
+ unsigned char Get(unsigned char seg, unsigned short ea) const;
+ void Set(unsigned short ea, unsigned char val);
+ void Set(unsigned char seg, unsigned short ea, unsigned char val);
+ void GetMem(unsigned short addr, unsigned char *data, int len, unsigned char seg=0) const;
+ void SetMem(unsigned short addr, const unsigned char *data, int len, unsigned char seg=0);
+ void ForceSet(unsigned short ea, unsigned char val, bool ro);
+ void SetSp(unsigned short SpHi, unsigned short SpLow);
+ void SetPc(unsigned short addr);
+ unsigned short GetPc(void) const { return pc; }
+ void PopPc(void);
+ void PopCr(void);
+ virtual void Stepper(void)=0;
+private:
+ unsigned int stats[256];
+ char addrBuff[32];
+ bool doDisAsm;
+ int disAsmLogClass;
+protected:
+ cLineBuff *loglb;
+ char * PADDR(unsigned char s, unsigned short ea);
+public:
+ c6805(void);
+ virtual ~c6805();
+ };
+
+#endif
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __LOG_NAGRA_H
+#define __LOG_NAGRA_H
+
+#include "log-sys.h"
+
+#define L_SYS 7
+#define L_SYS_EMU LCLASS(L_SYS,L_SYS_LASTDEF<<1)
+#define L_SYS_DISASM LCLASS(L_SYS,L_SYS_LASTDEF<<2)
+#define L_SYS_DISASM80 LCLASS(L_SYS,L_SYS_LASTDEF<<3)
+#define L_SYS_CPUSTATS LCLASS(L_SYS,L_SYS_LASTDEF<<4)
+#define L_SYS_MAP LCLASS(L_SYS,L_SYS_LASTDEF<<5)
+#define L_SYS_RAWEMM LCLASS(L_SYS,L_SYS_LASTDEF<<6)
+#define L_SYS_ALL LALL(L_SYS_RAWEMM)
+
+#define bprint(a) {fprintf(stdout, #a "="); BN_print_fp(stdout,a); fprintf(stdout,"\n");}
+
+#endif
+
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "nagra.h"
+#include "log-nagra.h"
+#include "log-core.h"
+
+static const struct LogModule lm_sys = {
+ (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SYS_DEFDEF|L_SYS_EMU|L_SYS_DISASM80|L_SYS_MAP)&LOPT_MASK,
+ "nagra",
+ { L_SYS_DEFNAMES,"emu","disasm","disasm80","cpuStats","map","rawemm" }
+ };
+ADD_MODULE(L_SYS,lm_sys)
+
+int minEcmTime=400; // ms
+
+#ifndef TESTER
+
+// -- cPlainKeyNagra -----------------------------------------------------------
+
+#define PLAINLEN_NAGRA_H 8
+#define PLAINLEN_NAGRA_I 16
+#define PLAINLEN_NAGRA_B 64
+#define PLAINLEN_NAGRA_IE 24
+#define PLAINLEN_NAGRA_BE 96
+
+static cPlainKeyTypeReg<cPlainKeyNagra,'N'> KeyReg;
+
+cPlainKeyNagra::cPlainKeyNagra(bool Super)
+:cDualKey(Super,true)
+{}
+
+bool cPlainKeyNagra::IsBNKey(void) const
+{
+ switch(keynr & C2MASK) {
+ // Nagra1
+ case MBC('M','1'):
+ case MBC('E','1'):
+ case MBC('N','1'):
+ case MBC('N','2'):
+ // Nagra2
+ case MBC(N2_MAGIC,N2_EMM_G_R):
+ case MBC(N2_MAGIC,N2_EMM_S_R):
+ case MBC(N2_MAGIC,N2_EMM_G_R|N2_EMM_SEL):
+ case MBC(N2_MAGIC,N2_EMM_S_R|N2_EMM_SEL):
+ return true;
+ }
+ return false;
+}
+
+int cPlainKeyNagra::ParseTag(const char *tag, const char * &line)
+{
+ int r=-1;
+ const int l=strlen(tag);
+ if(!strncasecmp(line,tag,l)) {
+ line+=l; r=0;
+ while(*line!=0 && isdigit(*line)) {
+ r=r*10 + *line-'0';
+ line++;
+ }
+ line=skipspace(line);
+ }
+ return r;
+}
+
+void cPlainKeyNagra::GetTagDef(int nr, int &romnr, int &pk, int &keytype)
+{
+ romnr =DEF_ROM;
+ pk =DEF_PK;
+ keytype=DEF_TYPE;
+ nr&=C2MASK;
+ if(nr=='V' || nr==MBC('N','2') || nr==MBC('M','1'))
+ pk=0; // different default
+}
+
+bool cPlainKeyNagra::Parse(const char *line)
+{
+ unsigned char sid[2];
+ const char *sline=line;
+ int len;
+ if(GetChar(line,&type,1) && (len=GetHex(line,sid,2,false))) {
+ type=toupper(type); id=Bin2Int(sid,len);
+ line=skipspace(line);
+ bool ok;
+ if(!strncasecmp(line,"NN",2)) {
+ line=skipspace(line+2);
+ unsigned char skeynr;
+ if((ok=GetHex(line,&skeynr,1)))
+ keynr=MBC(N2_MAGIC,skeynr);
+ }
+ else {
+ bool tags;
+ switch(toupper(*line)) {
+ case 'E':
+ case 'N': ok=GetChar(line,&keynr,2); tags=true; break;
+ case 'M': ok=GetChar(line,&keynr,2); tags=false; break;
+ case 'V': ok=GetChar(line,&keynr,1); tags=true; break;
+ default: {
+ unsigned char skeynr;
+ ok=GetHex(line,&skeynr,1); keynr=skeynr; tags=false; break;
+ }
+ }
+ if(ok) {
+ int romnr, pk, keytype;
+ GetTagDef(keynr,romnr,pk,keytype);
+ line=skipspace(line);
+ while(tags) {
+ int r;
+ if((r=ParseTag("ROM",line))>=0) {
+ if(r>0 && r<=15) romnr=r;
+ else PRINTF(L_CORE_LOAD,"nagrakey: ignoring bad ROM number %d",r);
+ }
+ else if((r=ParseTag("TYP",line))>=0) {
+ if(r>=0 && r<=1) keytype=r;
+ else PRINTF(L_CORE_LOAD,"nagrakey: ignoring bad key type %d",r);
+ }
+ else if((r=ParseTag("PK",line))>=0) {
+ if(r>=0 && r<=2) pk=r;
+ else PRINTF(L_CORE_LOAD,"nagrakey: ignoring bad PK key number %d",pk);
+ }
+ else {
+ tags=false;
+ if(pk!=0 && (keynr=='V' || keynr==MBC('N','2'))) {
+ pk=0;
+ PRINTF(L_CORE_LOAD,"nagrakey: auto-adjusting to PK0 for N2/V key");
+ }
+ if(romnr!=0 && keytype!=1) {
+ keytype=1;
+ PRINTF(L_CORE_LOAD,"nagrakey: auto-adjusting to TYP1 for ROM key");
+ }
+ }
+ }
+ if(IsBNKey() || keynr=='V') keynr=ADDC3(keynr,KEYSET(romnr,pk,keytype));
+ }
+ }
+
+ unsigned char skey[PLAINLEN_NAGRA_BE];
+ int keylen=PLAINLEN_NAGRA_BE;
+ len=GetHex(line,skey,keylen,false);
+ if( (!IsBNKey() && (len==PLAINLEN_NAGRA_H || len==PLAINLEN_NAGRA_I || len==PLAINLEN_NAGRA_IE))
+ || ( IsBNKey() && (len==PLAINLEN_NAGRA_B || len==PLAINLEN_NAGRA_BE)))
+ keylen=len;
+ if(len==keylen) {
+ if((keynr=='V' || keynr==MBC(N2_MAGIC,0x03) || keynr==MBC(N2_MAGIC,(0x03|N2_EMM_SEL))) && CheckNull(skey,len))
+ PRINTF(L_GEN_WARN,"nagrakey: FAKE verify keys will cause problems. Please remove them!");
+ SetBinKey(skey,len);
+ return true;
+ }
+ }
+ FormatError("nagra",sline);
+ return false;
+}
+
+cString cPlainKeyNagra::PrintKeyNr(void)
+{
+ if(C2(keynr)==N2_MAGIC)
+ return cString::sprintf("NN %.2X",keynr&0xFF);
+ int k=keynr & C2MASK;
+ if(!IsBNKey() && k!='V')
+ return cString::sprintf("%.2X",keynr);
+ char nr[32];
+ int q=0;
+ if(IsBNKey()) nr[q++]=(keynr>>8) & 0xff;
+ nr[q++]=keynr & 0xff;
+ nr[q]=0;
+ int romnr, pk, keytype;
+ GetTagDef(keynr,romnr,pk,keytype);
+ if(ROM(keynr) !=romnr ) q+=snprintf(nr+q,sizeof(nr)-q," ROM%d",ROM(keynr));
+ if(PK(keynr) !=pk ) q+=snprintf(nr+q,sizeof(nr)-q," PK%d",PK(keynr));
+ if(TYPE(keynr)!=keytype) q+=snprintf(nr+q,sizeof(nr)-q," TYP%d",TYPE(keynr));
+ return nr;
+}
+
+#endif // TESTER
+
+// -- cNagra -------------------------------------------------------------------
+
+cNagra::cNagra(void)
+{
+ BN_set_word(pubExp,3);
+}
+
+void cNagra::CreateRSAPair(const unsigned char *key, const unsigned char *data, BIGNUM *e, BIGNUM *m)
+{
+ // Calculate P and Q from data
+ cBN p,q;
+ CreatePQ(key,p,q);
+ ExpandPQ(p,q,data,e,m);
+}
+
+void cNagra::ExpandPQ(BIGNUM *p, BIGNUM *q, const unsigned char *data, BIGNUM *e, BIGNUM *m)
+{
+ // Calculate N=P*Q (modulus)
+ cBNctx ctx;
+ BN_mul(m,p,q,ctx);
+ if(data) BN_bin2bn(data,64,e); // set provided data as E1
+ else { // else calculate the 'official' one
+ // E = ( ( ( (P-1) * (Q-1) * 2) + 1) / 3)
+ BN_sub_word(p,1);
+ BN_sub_word(q,1);
+ BN_mul(e,p,q,ctx);
+ BN_mul_word(e,2);
+ BN_add_word(e,1);
+ BN_div_word(e,3);
+ }
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __NAGRA_NAGRA_H
+#define __NAGRA_NAGRA_H
+
+#include "system-common.h"
+#include "crypto.h"
+
+// ----------------------------------------------------------------
+
+#define SYSTEM_NAGRA 0x1800
+#define SYSTEM_NAGRA2 0x1801
+#define SYSTEM_NAGRA_BEV 0x1234
+
+extern int minEcmTime;
+
+// ----------------------------------------------------------------
+
+// Nagra1 defines
+#define DEF_TYPE 0
+#define DEF_PK 2
+#define DEF_ROM 0
+
+#define TYPE(keynr) (((keynr)>>16)&1)
+#define PK(keynr) (((keynr)>>17)&3)
+#define ROM(keynr) (((keynr)>>19)&15)
+#define KEYSET(rom,pk,type) ((((rom)&15)<<3)|(((pk)&3)<<1)|((type)&1))
+
+// Nagra2 defines
+#define N2_MAGIC 0x80
+#define N2_EMM_G_I 0x02
+#define N2_EMM_G_R 0x12
+#define N2_EMM_S_I 0x01
+#define N2_EMM_S_R 0x11
+#define N2_EMM_SEL 0x40
+#define N2_EMM_V 0x03
+
+class cPlainKeyNagra : public cDualKey {
+private:
+ int ParseTag(const char *tag, const char * &line);
+ void GetTagDef(int nr, int &romnr, int &pk, int &keytype);
+protected:
+ virtual bool IsBNKey(void) const;
+ virtual int IdSize(void) { return 4; }
+ virtual cString PrintKeyNr(void);
+public:
+ cPlainKeyNagra(bool Super);
+ virtual bool Parse(const char *line);
+ };
+
+// ----------------------------------------------------------------
+
+class cNagra {
+protected:
+ cRSA rsa;
+ cBN pubExp;
+ //
+ virtual void CreatePQ(const unsigned char *pk, BIGNUM *p, BIGNUM *q)=0;
+ void ExpandPQ(BIGNUM *p, BIGNUM *q, const unsigned char *data, BIGNUM *e, BIGNUM *m);
+ void CreateRSAPair(const unsigned char *key, const unsigned char *data, BIGNUM *e, BIGNUM *m);
+public:
+ cNagra(void);
+ virtual ~cNagra() {};
+ };
+
+#endif
--- /dev/null
+#
+# Nagra
+#
+TARGET = nagra
+OBJS = nagra.o nagra1.o nagra2.o cpu.o
+LIBS = -lcrypto
+CLEAN_RM = nagra2-prov.c
+
+nagra2-prov.c:
+ echo >$@ "/* generated file, do not edit */"
+ find -name "nagra2-[0-9][0-9][0-9][0-9].c" -printf '#include "%f"\n' >>$@
+ @rm .dependencies
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "system.h"
+#include "misc.h"
+#include "opts.h"
+#include "helper.h"
+
+#include "nagra.h"
+#include "cpu.h"
+#include "log-nagra.h"
+
+#define SYSTEM_NAME "Nagra"
+#define SYSTEM_PRI -10
+
+// -- cEmu ---------------------------------------------------------------------
+
+#define MAX_COUNT 200000
+
+class cEmu : public c6805 {
+protected:
+ int romNr, id;
+ char *romName, *romExtName, *eepromName;
+ //
+ int InitStart, InitEnd;
+ int EmmStart, EmmEnd, EmmKey0, EmmKey1;
+ int FindKeyStart, FindKeyEnd;
+ int MapAddr;
+ int Rc1H, Rc1L;
+ int EnsIrdChk, Cmd83Chk;
+ int SoftInt, StackHigh;
+ //
+ bool AddRom(unsigned short addr, unsigned short size, const char *name);
+ bool AddEeprom(unsigned short addr, unsigned short size, unsigned short otpSize, const char *name);
+ //
+ virtual bool InitSetup(void) { return true; }
+ virtual bool UpdateSetup(const unsigned char *emm) { return true; }
+ virtual bool MathMapHandler(void);
+public:
+ cEmu(void);
+ virtual ~cEmu();
+ bool Init(int RomNr, int Id);
+ bool GetOpKeys(const unsigned char *Emm, unsigned char *id, unsigned char *key0, unsigned char *key1);
+ bool GetPkKeys(const unsigned char *select, unsigned char *pkset);
+ bool Matches(int RomNr, int Id);
+ };
+
+cEmu::cEmu(void)
+{
+ romName=romExtName=eepromName=0;
+}
+
+cEmu::~cEmu()
+{
+ free(romName);
+ free(romExtName);
+ free(eepromName);
+}
+
+bool cEmu::AddRom(unsigned short addr, unsigned short size, const char *name)
+{
+ cMap *map=new cMapRom(addr,name);
+ return AddMapper(map,addr,size);
+}
+
+bool cEmu::AddEeprom(unsigned short addr, unsigned short size, unsigned short otpSize, const char *name)
+{
+ cMap *map=new cMapEeprom(addr,name,otpSize);
+ return AddMapper(map,addr,size);
+}
+
+bool cEmu::Matches(int RomNr, int Id)
+{
+ return (romNr==RomNr && id==Id);
+}
+
+bool cEmu::Init(int RomNr, int Id)
+{
+ romNr=RomNr; id=Id;
+ asprintf(&romName,"ROM%d.bin",romNr);
+ asprintf(&romExtName,"ROM%dext.bin",romNr);
+ asprintf(&eepromName,"eep%i_%02x.bin",romNr,(id&0xff00)>>8);
+ if(InitSetup()) {
+ ForceSet(EnsIrdChk, 0x81,true);
+ ForceSet(Cmd83Chk+0,0x98,true);
+ ForceSet(Cmd83Chk+1,0x9d,true);
+ if(SoftInt) {
+ Set(0x1ffc,SoftInt>>8);
+ Set(0x1ffd,SoftInt&0xff);
+ }
+ SetSp(StackHigh,0);
+ SetPc(InitStart);
+ ClearBreakpoints();
+ AddBreakpoint(InitEnd);
+ if(!Run(MAX_COUNT)) return true;
+ }
+ return false;
+}
+
+bool cEmu::GetOpKeys(const unsigned char *Emm, unsigned char *id, unsigned char *key0, unsigned char *key1)
+{
+ int keys=0;
+ if(UpdateSetup(Emm)) {
+ SetMem(0x0080,&Emm[0],64);
+ SetMem(0x00F8,&Emm[1],2);
+ SetPc(EmmStart);
+ ClearBreakpoints();
+ AddBreakpoint(EmmEnd);
+ AddBreakpoint(MapAddr);
+ AddBreakpoint(EmmKey0);
+ AddBreakpoint(EmmKey1);
+ while(!Run(MAX_COUNT)) {
+ unsigned short pc=GetPc();
+ if(pc==EmmKey0) {
+ GetMem(0x82,key0,8);
+ keys++;
+ }
+ if(pc==EmmKey1) {
+ GetMem(0x82,key1,8);
+ keys++;
+ }
+ if(pc==MapAddr) {
+ if(!MathMapHandler()) break;
+ PopPc(); // remove return address from stack
+ }
+ if(pc==EmmEnd) {
+ GetMem(0x00F8,id,2);
+ break;
+ }
+ }
+ }
+ return (keys==2);
+}
+
+bool cEmu::GetPkKeys(const unsigned char *select, unsigned char *pkset)
+{
+ Set(0x0081,select[2]<<7);
+ SetMem(0x00F8,select,3);
+ SetPc(FindKeyStart);
+ ClearBreakpoints();
+ AddBreakpoint(FindKeyEnd);
+ while(!Run(MAX_COUNT)) {
+ unsigned short pc=GetPc();
+ if(pc==FindKeyEnd) {
+ if(!cc.c) {
+ PRINTF(L_SYS_EMU,"Updating PK keys");
+ unsigned short loc=(Get(Rc1H)<<8)+Get(Rc1L);
+ for(int i=0; i<45; i+=15) GetMem(loc+4+i,pkset+i,15);
+ return true;
+ }
+ else {
+ PRINTF(L_SYS_EMU,"Updating PK keys failed. Used a correct EEPROM image for provider %04x ?",((select[0]<<8)|select[1]));
+ break;
+ }
+ }
+ }
+ return false;
+}
+
+bool cEmu::MathMapHandler(void)
+{
+ PRINTF(L_SYS_EMU,"Unsupported math call $%02x in ROM %d, please report",a,romNr);
+ return false;
+}
+
+// -- cEmuRom3Core -------------------------------------------------------------
+
+class cEmuRom3Core : public cEmu {
+protected:
+ virtual void Stepper(void);
+ bool CoreInitSetup(void);
+ bool DoMaps(bool hasExt);
+ };
+
+void cEmuRom3Core::Stepper(void)
+{
+ int rnd=random();
+ unsigned char mem4=Get(0x04);
+ if(!bitset(mem4,2)) {
+ Set(0x06,(rnd&0xff00)>>8);
+ Set(0x07,rnd&0xff);
+ }
+}
+
+bool cEmuRom3Core::CoreInitSetup(void)
+{
+ ForceSet(0x0001,1<<3,true);
+ Set(0x0002,0x03);
+ Set(0x004e,0x4B);
+ return true;
+}
+
+bool cEmuRom3Core::DoMaps(bool hasExt)
+{
+ // Eeprom & ROMext are non-fatal also they are required for some providers
+ if(hasExt) AddRom(0x2000,0x2000,romExtName);
+ AddEeprom(0xE000,0x1000,0x20,eepromName);
+ return AddRom(0x4000,0x4000,romName);
+}
+
+// -- cEmuRom3 -----------------------------------------------------------------
+
+class cEmuRom3 : public cEmuRom3Core {
+protected:
+ virtual bool InitSetup(void);
+ virtual bool UpdateSetup(const unsigned char *emm);
+public:
+ cEmuRom3(void);
+ };
+
+cEmuRom3::cEmuRom3(void)
+{
+ InitStart=0x4000;
+ InitEnd =0x734b;
+ EmmStart =0x5a82;
+ EmmEnd =0x67db;
+ EmmKey0 =0x617c;
+ EmmKey1 =0x6184;
+ FindKeyStart=0x6133;
+ FindKeyEnd =0x6147;
+ MapAddr =0x2800;
+ Rc1H =0x0024;
+ Rc1L =0x0025;
+ EnsIrdChk=0x6437;
+ Cmd83Chk =0x6431;
+ SoftInt =0x0000;
+ StackHigh=0x7f;
+}
+
+bool cEmuRom3::InitSetup(void)
+{
+ return DoMaps(true) && CoreInitSetup();
+}
+
+bool cEmuRom3::UpdateSetup(const unsigned char *emm)
+{
+ return CoreInitSetup();
+}
+
+// -- cEmuRom7 -----------------------------------------------------------------
+
+class cEmuRom7 : public cEmuRom3Core {
+protected:
+ virtual bool InitSetup(void);
+ virtual bool UpdateSetup(const unsigned char *emm);
+public:
+ cEmuRom7(void);
+ };
+
+cEmuRom7::cEmuRom7(void)
+{
+ InitStart=0x4000;
+ InitEnd =0x7b68;
+ EmmStart =0x482b;
+ EmmEnd =0x6146;
+ EmmKey0 =0x4f2a;
+ EmmKey1 =0x4f32;
+ FindKeyStart=0x4ee4;
+ FindKeyEnd =0x4ef5;
+ MapAddr =0x200f;
+ Rc1H =0x0028;
+ Rc1L =0x0029;
+ EnsIrdChk=0x51df;
+ Cmd83Chk =0x51d9;
+ SoftInt =0x4008;
+ StackHigh=0x7f;
+}
+
+bool cEmuRom7::InitSetup(void)
+{
+ return DoMaps(false) && CoreInitSetup();
+}
+
+bool cEmuRom7::UpdateSetup(const unsigned char *emm)
+{
+ SetMem(0x01A2,&emm[1],2);
+ return true;
+}
+
+// -- cEmuRom10Core ------------------------------------------------------------
+
+class cEmuRom10Core : public cEmu {
+protected:
+ struct Map { unsigned char A[64], B[64], C[64], D[4], opSize; } map;
+ //
+ virtual void Stepper(void);
+ bool DoMaps(bool hasExt, int romSize);
+ bool CoreInitSetup(void);
+ bool CoreUpdateSetup(const unsigned char *emm);
+ };
+
+void cEmuRom10Core::Stepper(void)
+{
+ int rnd=random();
+ unsigned char mem7=Get(0x07);
+ if(cc.i) mem7&=0xFD; else mem7|=0x02;
+ Set(0x07,mem7);
+ if(bitset(mem7,1) && bitset(mem7,7)) {
+ Set(0x05,(rnd&0xFF00)>>8);
+ Set(0x06,rnd&0xFF);
+ }
+}
+
+bool cEmuRom10Core::DoMaps(bool hasExt, int romSize)
+{
+ // Eeprom & ROMext are non-fatal also they are required for some providers
+ if(hasExt) AddRom(0x2000,0x2000,romExtName);
+ AddEeprom(0xC000,0x2000,0x40,eepromName);
+ return AddRom(0x4000,romSize,romName);
+}
+
+bool cEmuRom10Core::CoreInitSetup(void)
+{
+ ForceSet(0x01,0x13,true);
+ Set(0x02,0x3);
+ Set(0x07,0xFF);
+ return true;
+}
+
+bool cEmuRom10Core::CoreUpdateSetup(const unsigned char *emm)
+{
+ SetMem(0x01A2,&emm[1],2);
+ SetMem(0x0307,&emm[1],2);
+ Set(0x0300,emm[5]);
+ Set(0x0301,emm[2]);
+ return true;
+}
+
+// -- cEmuRom10 ----------------------------------------------------------------
+
+class cEmuRom10 : public cEmuRom10Core {
+protected:
+ virtual bool InitSetup(void);
+ virtual bool UpdateSetup(const unsigned char *emm);
+ virtual bool MathMapHandler(void);
+public:
+ cEmuRom10(void);
+ };
+
+cEmuRom10::cEmuRom10(void)
+{
+ InitStart=0x4000;
+ InitEnd =0x81ca;
+ EmmStart =0x6a71;
+ EmmEnd =0x81f7;
+ EmmKey0 =0x7172;
+ EmmKey1 =0x717a;
+ FindKeyStart=0x712c;
+ FindKeyEnd =0x713d;
+ MapAddr =0x2020;
+ Rc1H =0x004a;
+ Rc1L =0x004b;
+ EnsIrdChk=0x7427;
+ Cmd83Chk =0x7421;
+ SoftInt =0x4004;
+ StackHigh=0x3ff;
+}
+
+bool cEmuRom10::InitSetup(void)
+{
+ return DoMaps(false,0x5A00) && CoreInitSetup();
+}
+
+bool cEmuRom10::UpdateSetup(const unsigned char *emm)
+{
+ Set(0x006e,0x4B);
+ return CoreUpdateSetup(emm);
+}
+
+bool cEmuRom10::MathMapHandler(void)
+{
+ PRINTF(L_SYS_EMU,"math call: $%02x",a);
+ switch(a) {
+ case 0x02:
+ switch(Get(0x41)) {
+ case 0: map.opSize=0x04; break;
+ case 1: map.opSize=0x20; break;
+ case 2: map.opSize=0x24; break;
+ case 3: map.opSize=0x30; break;
+ case 4: map.opSize=0x34; break;
+ case 5:
+ default: map.opSize=0x40; break;
+ }
+ return true;
+
+ case 0x0e ... 0x10:
+ {
+ const unsigned short addr=(Get(0x4d)<<8)|Get(0x4e);
+ unsigned char tmp[64];
+ GetMem(addr,tmp,map.opSize);
+ unsigned char *reg;
+ switch(a) {
+ case 0x0e: reg=map.A; break;
+ case 0x0f: reg=map.B; break;
+ case 0x10: reg=map.C; break;
+ default: return false;
+ }
+ SetMem(addr,reg,map.opSize);
+ memcpy(reg,tmp,map.opSize);
+ Set(0x41,map.opSize);
+ return true;
+ }
+
+ case 0x29:
+ Set(0x120,1);
+ return true;
+ }
+ return cEmu::MathMapHandler();
+}
+
+// -- cEmuRom11 ----------------------------------------------------------------
+
+class cEmuRom11 : public cEmuRom10Core {
+protected:
+ virtual bool InitSetup(void);
+ virtual bool UpdateSetup(const unsigned char *emm);
+public:
+ cEmuRom11(void);
+ };
+
+cEmuRom11::cEmuRom11(void)
+{
+ InitStart=0x4000;
+ InitEnd =0x405b;
+ EmmStart =0x5865;
+ EmmEnd =0x9990;
+ EmmKey0 =0x5f66;
+ EmmKey1 =0x5f6e;
+ FindKeyStart=0x5f20;
+ FindKeyEnd =0x5f31;
+ MapAddr =0x2020;
+ Rc1H =0x004a;
+ Rc1L =0x004b;
+ EnsIrdChk=0x621b;
+ Cmd83Chk =0x6215;
+ SoftInt =0x4004;
+ StackHigh=0x3ff;
+}
+
+bool cEmuRom11::InitSetup(void)
+{
+ return DoMaps(false,0x8000) && CoreInitSetup();
+}
+
+bool cEmuRom11::UpdateSetup(const unsigned char *emm)
+{
+ Set(0x0068,0x4B);
+ return CoreUpdateSetup(emm);
+}
+
+// -- cNagraDES ----------------------------------------------------------------
+
+class cNagraDES {
+private:
+ cDes des;
+protected:
+ void Decrypt(const unsigned char *data, const unsigned char *key, unsigned char *out, bool desmod=false);
+ void Crypt(const unsigned char *data, const unsigned char *key, unsigned char *out);
+ bool SigCheck(const unsigned char *block, const unsigned char *sig, const unsigned char *vkey, const int rounds);
+};
+
+void cNagraDES::Decrypt(const unsigned char *data, const unsigned char *key, unsigned char *out, bool desmod)
+{
+ unsigned char cardkey[8];
+ memcpy(cardkey,key,8);
+ RotateBytes(cardkey,8);
+ memcpy(out,data,8);
+ if(!desmod) RotateBytes(out,8);
+ des.Des(out,cardkey,NAGRA_DES_DECR);
+ if(!desmod) RotateBytes(out,8);
+}
+
+void cNagraDES::Crypt(const unsigned char *data, const unsigned char *key, unsigned char *out)
+{
+ unsigned char cardkey[8];
+ memcpy(cardkey,key,8);
+ RotateBytes(cardkey,8);
+ memcpy(out,data,8);
+ RotateBytes(out,8);
+ des.Des(out,cardkey,NAGRA_DES_ENCR);
+ RotateBytes(out,8);
+}
+
+bool cNagraDES::SigCheck(const unsigned char *block, const unsigned char *sig, const unsigned char *vkey, const int rounds)
+{
+ unsigned char hash[8];
+ memcpy(hash,vkey,8);
+ for(int j=0; j<rounds; j++) {
+ unsigned char cr[8];
+ Crypt(block+j*8,hash,cr);
+ xxor(hash,8,cr,&block[j*8]);
+ }
+ return (0==memcmp(hash,sig,8));
+}
+
+// -- cNagra1 ------------------------------------------------------------------
+
+class cNagra1 : public cNagra, public cNagraDES {
+protected:
+ virtual void CreatePQ(const unsigned char *key, BIGNUM *p, BIGNUM *q);
+ bool DecryptECM(const unsigned char *in, unsigned char *out, const unsigned char *vkey, int len, BIGNUM *e1, BIGNUM *n1, BIGNUM *n2);
+ bool DecryptEMM(const unsigned char *in, unsigned char *out, const unsigned char *vkey, int len, BIGNUM *e1, BIGNUM *n1, BIGNUM *n2);
+ };
+
+bool cNagra1::DecryptECM(const unsigned char *in, unsigned char *out, const unsigned char *vkey, int len, BIGNUM *e1, BIGNUM *n1, BIGNUM *n2)
+{
+ cBN result;
+ if(rsa.RSA(result,&in[2],len,pubExp,n2)<=0) {
+ PRINTF(L_SYS_CRYPTO,"error decrypting ECM (round 1)");
+ return false;;
+ }
+ cBN mod;
+ BN_set_word(mod,in[0]>>4);
+ BN_lshift(mod,mod,508);
+ BN_add(result,result,mod);
+ if(rsa.RSA(out,64,result,e1,n1,false)!=64) {
+ PRINTF(L_SYS_CRYPTO,"error: result of ECM decryption is not 64 bytes");
+ return false;
+ }
+ if(vkey && !SigCheck(out,&out[56],vkey,7)) {
+ PRINTF(L_SYS_CRYPTO,"ECM decryption failed");
+ return false;
+ }
+ return true;
+}
+
+bool cNagra1::DecryptEMM(const unsigned char *in, unsigned char *out, const unsigned char *vkey, int len, BIGNUM *e1, BIGNUM *n1, BIGNUM *n2)
+{
+ cBN result;
+ if(rsa.RSA(result,&in[9],len,pubExp,n2)<=0) {
+ PRINTF(L_SYS_CRYPTO,"error decrypting EMM (round 1)");
+ return false;;
+ }
+ cBN mod;
+ BN_set_word(mod,in[0]>>6);
+ BN_lshift(mod,mod,510);
+ BN_add(result,result,mod);
+ if(rsa.RSA(out,64,result,e1,n1,false)!=64) {
+ PRINTF(L_SYS_CRYPTO,"error: result of EMM decryption is not 64 bytes");
+ return false;
+ }
+ if(!vkey || SigCheck(out,&in[1],vkey,8))
+ return true;
+
+ // We need to use signature exchange method (7blocks emm,1 block signature)
+ PRINTF(L_SYS_CRYPTO,"warning: signature failed,trying signature exchange method");
+ unsigned char buf[8];
+ RotateBytes(buf,&in[1],sizeof(buf));
+ cBN newdata, newsig;
+ BN_bin2bn(buf,sizeof(buf),newdata);
+ BN_copy(newsig,result);
+ BN_mask_bits(newsig,64);
+ BN_bn2bin(newsig,buf);
+ RotateBytes(buf,sizeof(buf));
+
+ BN_rshift(result,result,64); // delete the lower 64 bits,we saved it
+ BN_lshift(result,result,64); // before as the new 64 bit signature
+ BN_add(result,result,newdata);
+
+ if(rsa.RSA(out,64,result,e1,n1)!=64) {
+ PRINTF(L_SYS_CRYPTO,"error: result of EMM decryption is not 64 bytes");
+ return false;
+ }
+ if(vkey && !SigCheck(out,buf,vkey,8)) {
+ PRINTF(L_SYS_CRYPTO,"fatal: signature failed in signature exchange method");
+ return false;
+ }
+ return true;
+}
+
+void cNagra1::CreatePQ(const unsigned char *key, BIGNUM *p, BIGNUM *q)
+{
+ // Make des_key
+ unsigned char des_data[32];
+ unsigned char des_key[8], des_tmp[8];
+ memcpy(des_data,key,8);
+ RotateBytes(des_tmp,key,8);
+ des_tmp[7]=0x00;
+ Decrypt(des_data,des_tmp,des_key,true);
+ RotateBytes(des_key,8);
+ des_key[7]=0x00;
+
+ // Calculate P
+ for(int i=0; i<4; i++) {
+ const int off=i*8;
+ memcpy(&des_data[off],&key[4],8);
+ des_data[off]^=i;
+ Decrypt(&des_data[off],des_key,des_tmp,true);
+ memcpy(&des_data[off],des_tmp,8);
+ }
+ BN_bin2bn(des_data,32,p);
+ BN_add_word(p,(key[12]<<4)|((key[13]>>4)&0x0f));
+ BN_set_bit(p,(BN_num_bytes(p)*8)-1);
+
+ // Calculate Q
+ for(int i=0; i<4; i++) {
+ const int off=i*8;
+ memcpy(&des_data[off],&key[4],8);
+ des_data[off]^=(i+4);
+ Decrypt(&des_data[off],des_key,des_tmp,true);
+ memcpy(&des_data[off],des_tmp,8);
+ }
+ BN_bin2bn(des_data,32,q);
+ BN_add_word(q,((key[13]&0x0f)<<8)|key[14]);
+ BN_set_bit(q,(BN_num_bytes(q)*8)-1);
+}
+
+#ifndef TESTER
+
+// -- cSystemNagra -------------------------------------------------------------
+
+class cSystemNagra : public cSystem, public cNagra1 {
+private:
+ unsigned char mecmTable[256];
+ cEmu *emu;
+ //
+ void WriteTable(unsigned char *from, int off);
+public:
+ cSystemNagra(void);
+ virtual ~cSystemNagra();
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+ virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+ };
+
+cSystemNagra::cSystemNagra(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+ emu=0;
+ memset(mecmTable,0,sizeof(mecmTable));
+ hasLogger=true;
+}
+
+cSystemNagra::~cSystemNagra()
+{
+ delete emu;
+}
+
+void cSystemNagra::WriteTable(unsigned char *from, int off)
+{
+ off&=0xFF;
+ if(off+16<256) memcpy(mecmTable+off,from,16);
+ else {
+ int l=256-off;
+ memcpy(mecmTable+off,from,l);
+ memcpy(mecmTable,from+l,16-l);
+ }
+}
+
+bool cSystemNagra::ProcessECM(const cEcmInfo *ecm, unsigned char *data)
+{
+ int cmdLen=data[4];
+ int id=(data[5]*256)+data[6];
+ cTimeMs minTime;
+ if(data[3]!=0x03) {
+ PRINTF(L_SYS_ECM,"invalid ECM");
+ return false;
+ }
+ data+=7;
+ if(data[0]!=0x10) {
+ PRINTF(L_SYS_ECM,"no ECM data available");
+ return false;
+ }
+ const int ecmLen=data[1];
+ const int verType=(data[2]>>3)&1;
+ const int keynr=(data[2]>>4)&1;
+ const int ecmParm=data[2]&7;
+ bool decrypt;
+ if(ecmParm==7) decrypt=false;
+ else if(ecmParm==5) decrypt=true;
+ else {
+ PRINTF(L_SYS_ECM,"unknown ecmParm, ignoring ECM");
+ return false;
+ }
+
+ cKeySnoop ks(this,'N',id,keynr);
+ unsigned char sessionKey[8], verifyKey[8];
+ bool hasVerifyKey=false;
+ cPlainKey *pk;
+ if((pk=keys.FindKey('N',id,ADDC3('V',KEYSET(0,0,verType)),sizeof(verifyKey)))) {
+ pk->Get(verifyKey);
+ hasVerifyKey=true;
+ }
+ else if(doLog) PRINTF(L_SYS_KEY,"missing %04X V TYP%d key (non-fatal)",id,verType);
+ if(!(pk=keys.FindKey('N',id,keynr,sizeof(sessionKey)))) return false;
+ pk->Get(sessionKey);
+
+ const int desLen=(ecmLen-9) & ~7; // datalen - signature - ks byte
+ unsigned char decrypted[desLen];
+ for(int i=(desLen/8)-1; decrypt && i>=0; i--) {
+ const int off=i*8;
+ Decrypt(data+11+off,sessionKey,decrypted+off);
+ }
+ if(decrypt && hasVerifyKey && !SigCheck(decrypted,data+3,verifyKey,desLen/8)) {
+ PRINTF(L_SYS_ECM,"signature check failed in DES decrypt");
+ return false;
+ }
+ int cwEvenMecmIndex=-1, cwOddMecmIndex=-1;
+ switch(decrypted[0]) {
+ case 0x10: // Whole CW
+ cwOddMecmIndex=decrypted[1];
+ memcpy(cw+8,&decrypted[2],8);
+ cwEvenMecmIndex=decrypted[10];
+ memcpy(cw,&decrypted[11],8);
+ break;
+ case 0x11: // Odd CW
+ cwOddMecmIndex=decrypted[1];
+ memcpy(cw+8, &decrypted[2],8);
+ break;
+ case 0x12: // Even CW
+ cwEvenMecmIndex=decrypted[1];
+ memcpy(cw,&decrypted[2],8);
+ break;
+ default: PRINTF(L_SYS_ECM,"failed to get CW"); return false;
+ }
+
+ const unsigned char * const mecmData=data+(ecmLen+2);
+ const int mecmRSALen=mecmData[1]-4;
+ if((cmdLen-(ecmLen+2))>64 && (*mecmData==0x20 || *mecmData==0x39)) {
+ if(mecmRSALen!=64) {
+ if(mecmRSALen>64 && doLog)
+ PRINTF(L_SYS_ECM,"ECM too big (len: %d)",mecmRSALen);
+ return false;
+ }
+
+ const int mecmProvId=mecmData[2]*256+mecmData[3];
+ const int keyType=(mecmData[4]>>3)&1;
+ const int pkey=mecmData[4]&3;
+
+ cBN e1, n1, n2;
+ cPlainKey *pk;
+ if(!(pk=keys.FindKey('N',mecmProvId,MBC3('E','1',KEYSET(0,pkey,keyType)),-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04x E1 PK%d TYP%d key",mecmProvId,pkey,keyType);
+ return false;
+ }
+ pk->Get(e1);
+ if(!(pk=keys.FindKey('N',mecmProvId,MBC3('N','1',KEYSET(0,pkey,keyType)),-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04x N1 PK%d TYP%d key",mecmProvId,pkey,keyType);
+ return false;
+ }
+ pk->Get(n1);
+ if(!(pk=keys.FindKey('N',mecmProvId,MBC3('N','2',KEYSET(0,0,keyType)),-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04x N2 TYP%d key",mecmProvId,keyType);
+ return false;
+ }
+ pk->Get(n2);
+ hasVerifyKey=false;
+ if((pk=keys.FindKey('N',mecmProvId,ADDC3('V',KEYSET(0,0,keyType)),sizeof(verifyKey)))) {
+ pk->Get(verifyKey);
+ hasVerifyKey=true;
+ }
+ else if(doLog) PRINTF(L_SYS_KEY,"missing %04x V TYP%d key (non-fatal)",mecmProvId,keyType);
+ unsigned char decrMecmData[mecmRSALen];
+ if(!DecryptECM(&mecmData[4],decrMecmData,hasVerifyKey?verifyKey:0,mecmRSALen,e1,n1,n2))
+ return false;
+
+ if(*mecmData==0x39 || (*mecmData==0x20 && (mecmProvId&0xFE00)==0x4800)) {
+ unsigned char xor_table[64];
+ for(int i=sizeof(xor_table)-1; i>=0; i--) xor_table[i]=63-i;
+ CreateRSAPair(&decrMecmData[24],xor_table,e1,n1);
+
+ // new XOR table for MECM data
+ cBNctx ctx;
+ cBN x;
+ BN_mod_exp(x,e1,pubExp,n1,ctx);
+ int l=sizeof(xor_table)-BN_num_bytes(x);
+ memset(xor_table,0,l);
+ BN_bn2bin(x,xor_table+l);
+ RotateBytes(xor_table,sizeof(xor_table));
+
+ // And finally, new MECM table
+ for(int i=39; i<mecmRSALen; i++) decrMecmData[i]^=xor_table[i-39];
+ memcpy(&decrMecmData[3],&decrMecmData[39],mecmRSALen-39);
+ }
+
+ if(decrMecmData[0]==0x2F && decrMecmData[1]==mecmData[2] && decrMecmData[2]==mecmData[3])
+ WriteTable(decrMecmData+4,decrMecmData[3]*2);
+ }
+
+ if(cwOddMecmIndex>=0 && cwOddMecmIndex<0x80) {
+ const int d=cwOddMecmIndex*2;
+ for(int i=0 ; i<8 ; i++) cw[i+8]^=mecmTable[(d+i)&0xFF]; // odd
+ }
+ if(cwEvenMecmIndex>=0 && cwEvenMecmIndex<0x80) {
+ const int d=cwEvenMecmIndex*2;
+ for(int i=0 ; i<8 ; i++) cw[i]^=mecmTable[(d+i)&0xFF]; // even
+ }
+ ks.OK(pk);
+ int i=minEcmTime-minTime.Elapsed();
+ if(i>0) cCondWait::SleepMs(i);
+
+ return true;
+}
+
+void cSystemNagra::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+ const int id=buffer[10]*256+buffer[11];
+ static const unsigned char tester[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x4B };
+ if(memcmp(&buffer[3],tester,sizeof(tester)) || SCT_LEN(buffer)<(12+9+64)) {
+ PRINTF(L_SYS_EMM,"%d: not a valid EMM structure",CardNum());
+ return;
+ }
+ const int pkey=(buffer[12]&3);
+ cPlainKey *pk;
+ cBN e1, n1, n2;
+ if(!(pk=keys.FindKey('N',id,MBC3('E','1',KEYSET(0,pkey,0)),-1))) return;
+ pk->Get(e1);
+ if(!(pk=keys.FindKey('N',id,MBC3('N','1',KEYSET(0,pkey,0)),-1))) return;
+ pk->Get(n1);
+ unsigned char v[8];
+ bool hasVerifyKey=false;
+ if((pk=keys.FindKey('N',id,ADDC3('V',KEYSET(0,0,0)),sizeof(v)))) {
+ pk->Get(v);
+ hasVerifyKey=true;
+ }
+ int mode=(buffer[12]&8) ? 0:2;
+ unsigned char emmdata[64];
+ bool r;
+ do {
+ switch(mode) {
+ case 0: // get the ROM10 specific management key
+ if(!(pk=keys.FindKey('N',id,MBC3('N','2',KEYSET(10,0,1)),-1))) return;
+ pk->Get(n2);
+ mode=1; break;
+ case 1: // get the ROM11 specific management keys
+ if(!(pk=keys.FindKey('N',id,MBC3('E','1',KEYSET(11,pkey,1)),-1))) return;
+ pk->Get(e1);
+ if(!(pk=keys.FindKey('N',id,MBC3('N','1',KEYSET(11,pkey,1)),-1))) return;
+ pk->Get(n1);
+ if(!(pk=keys.FindKey('N',id,MBC3('N','2',KEYSET(11,0,1)),-1))) return;
+ pk->Get(n2);
+ hasVerifyKey=false;
+ if((pk=keys.FindKey('N',id,ADDC3('V',KEYSET(11,0,1)),sizeof(v)))) {
+ pk->Get(v);
+ hasVerifyKey=true;
+ }
+ mode=-1; break;
+ case 2: // get the universal management key
+ if(!(pk=keys.FindKey('N',id,MBC3('N','2',KEYSET(0,0,0)),-1))) return;
+ pk->Get(n2);
+ mode=-1; break;
+ }
+ r=DecryptEMM(&buffer[12],emmdata,hasVerifyKey?v:0,64,e1,n1,n2);
+ } while(!r && mode>=0);
+ if(r) {
+ HEXDUMP(L_SYS_RAWEMM,emmdata,sizeof(emmdata),"Nagra1 RAWEMM");
+ unsigned int i=0;
+ bool gotKeys=false;
+ unsigned char key0[8], key1[8];
+ int keyId=(emmdata[i+1]<<8)+emmdata[i+2];
+ switch(emmdata[i]) { // Check filter type
+ case 0x00: // One card
+ i+=7; break;
+ case 0x01 ... 0x20: // Group of cards
+ i+=emmdata[i]+0x7; break;
+ case 0x3e:
+ i+=6; break;
+ case 0x3d: // All cards with same system ID
+ case 0x3f:
+ i+=3; break;
+ }
+ int nrKeys=0;
+ while(i<sizeof(emmdata)) {
+ if((emmdata[i]&0xF0)==0xF0) { // Update with CPU code
+ const int romNr=emmdata[i]&0x0F;
+ if(!emu || !emu->Matches(romNr,id)) {
+ delete emu; emu=0;
+ PRINTF(L_SYS_EMM,"%d: setting defaults for ROM %d",CardNum(),romNr);
+ switch(romNr) {
+ case 3: emu=new cEmuRom3; break;
+ case 7: emu=new cEmuRom7; break;
+ case 10: emu=new cEmuRom10; break;
+ case 11: emu=new cEmuRom11; break;
+ default: PRINTF(L_SYS_EMM,"%d: unsupported ROM",CardNum()); return;
+ }
+ if(!emu || !emu->Init(romNr,id)) {
+ delete emu; emu=0;
+ PRINTF(L_SYS_EMM,"%d: initialization failed for ROM %d",CardNum(),romNr);
+ return;
+ }
+ }
+ unsigned char ki[2];
+ if((gotKeys=emu->GetOpKeys(emmdata,ki,key0,key1))) {
+ keyId=(ki[0]<<8)+ki[1];
+ PRINTF(L_SYS_EMM,"%d: got keys for %04X (ROM %d)",CardNum(),keyId,romNr);
+ }
+ unsigned char select[3], pkset[3][15];
+ select[0]=(keyId>>8)|0x01; // always high id for ECM RSA keys
+ select[1]=keyId&0xFF;
+ select[2]=0; // type 0
+ if(emu->GetPkKeys(&select[0],&pkset[0][0])) {
+ int pkKeyId=((select[0]<<8)+select[1]);
+ PRINTF(L_SYS_EMM,"%d: got PK keys for %04X (ROM %d)",CardNum(),pkKeyId,romNr);
+ for(int i=0; i<3; i++) {
+ CreateRSAPair(pkset[i],0,e1,n1);
+ FoundKey();
+ if(keys.NewKey('N',pkKeyId,ADDC3(MBC('N','1'),KEYSET(0,i,0)),n1,64)) NewKey();
+ FoundKey();
+ if(keys.NewKey('N',pkKeyId,ADDC3(MBC('E','1'),KEYSET(0,i,0)),e1,64)) NewKey();
+ }
+ cLoaders::SaveCache();
+ }
+ break; // don't process other nanos
+ }
+ else if(emmdata[i]==0x60) { // NULL nano
+ i+=2;
+ }
+ else if(emmdata[i]==0x00) {
+ i++;
+ }
+ else if(emmdata[i]==0x81) {
+ i++;
+ }
+ else if(emmdata[i]==0x83) {
+ keyId=(emmdata[i+1]<<8)+emmdata[i+2];
+ i+=3;
+ }
+ else if(emmdata[i]==0x42) { // plain Key
+ if(emmdata[i+1]==0x05) memcpy(key0,&emmdata[i+2],sizeof(key0));
+ else memcpy(key1,&emmdata[i+2],sizeof(key1));
+ i+=10;
+ if(++nrKeys==2) {
+ gotKeys=true;
+ PRINTF(L_SYS_EMM,"%d: got keys for %04X (plain)",CardNum(),keyId);
+ break;
+ }
+ }
+ else {
+ PRINTF(L_SYS_EMM,"%d: ignored nano %02x",CardNum(),emmdata[i]);
+ break;
+ }
+ }
+ if(gotKeys) {
+ FoundKey();
+ if(keys.NewKey('N',keyId,00,key0,8)) NewKey();
+ FoundKey();
+ if(keys.NewKey('N',keyId,01,key1,8)) NewKey();
+ cLoaders::SaveCache();
+ }
+ }
+}
+
+// -- cSystemLinkNagra ---------------------------------------------------------
+
+static const tI18nPhrase Phrases[] = {
+ { "Nagra: min. ECM processing time",
+ "Nagra: min. ECM Bearbeitungszeit",
+ "",
+ "",
+ "Nagra: min. ECM bewerkingstijd",
+ "",
+ "Nagra: min. durée du processus ECM",
+ "",
+ "Nagra: Min. ECM-prosessointiaika",
+ "Nagra: min. czas przetwarzania ECM",
+ "",
+ "",
+ "",
+ },
+ { NULL }
+ };
+
+class cSystemLinkNagra : public cSystemLink {
+public:
+ cSystemLinkNagra(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemNagra; }
+ };
+
+static cSystemLinkNagra staticInitN1;
+
+cSystemLinkNagra::cSystemLinkNagra(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ opts=new cOpts(SYSTEM_NAME,1);
+ opts->Add(new cOptInt("MinEcmTime","Nagra: min. ECM processing time",&minEcmTime,0,5000));
+ Feature.NeedsKeyFile();
+ Feature.AddPhrases(Phrases);
+}
+
+bool cSystemLinkNagra::CanHandle(unsigned short SysId)
+{
+ return SysId==SYSTEM_NAGRA; // || SysId==SYSTEM_NAGRA_BEV;
+}
+
+#endif //TESTER
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+// -- cAuxSrv ------------------------------------------------------------------
+
+/*
+#define HAS_AUXSRV
+
+static int auxPort=7777;
+static char auxAddr[80]="localhost";
+static char auxPassword[250]="auxserver";
+
+class cAuxSrv : public cMutex {
+private:
+ cNetSocket so;
+ //
+ bool Login(void);
+public:
+ cAuxSrv(void);
+ bool Map(int map, unsigned char *data, int len, int outlen);
+ };
+
+cAuxSrv::cAuxSrv(void)
+:so(DEFAULT_CONNECT_TIMEOUT,7,DEFAULT_IDLE_TIMEOUT)
+{}
+
+bool cAuxSrv::Login()
+{
+ unsigned char buff[256];
+ PRINTF(L_SYS_MAP,"auxsrv: connecting to %s:%d",auxAddr,auxPort);
+ if(so.Connect(auxAddr,auxPort)) {
+ buff[0]=0xA7;
+ buff[1]=0x7A;
+ buff[2]=0;
+ int l=strlen(auxPassword);
+ buff[3]=l;
+ memcpy(&buff[4],auxPassword,l);
+ buff[4+l]=0xFF;
+ if(so.Write(buff,l+5)==l+5) return true;
+ PRINTF(L_SYS_MAP,"auxsrv: login write failed");
+ }
+ so.Disconnect();
+ return false;
+}
+
+bool cAuxSrv::Map(int map, unsigned char *data, int len, int outlen)
+{
+ if(len>500 || outlen>500) return false;
+ cMutexLock lock(this);
+ if(!so.Connected() && !Login()) return false;
+ PRINTF(L_SYS_MAP,"auxsrv: calling map%02x",map);
+ unsigned char buff[512];
+ buff[0]=0xA7;
+ buff[1]=0x7A;
+ buff[2]=((len+1)>>8) & 0xff;
+ buff[3]=(len+1)&0xff;
+ buff[4]=map;
+ memcpy(&buff[5],data,len);
+ buff[len+5]=0xFF;
+ if(so.Write(buff,len+6)==len+6) {
+ if((len=so.Read(buff,sizeof(buff)))>0) {
+ if(buff[0]==0x7A && buff[1]==0xA7) {
+ if(buff[4]==0x00) {
+ int l=buff[2]*256+buff[3];
+ if(len>=l+5 && l==outlen+1) {
+ if(buff[l+4]==0xFF) {
+ memcpy(data,buff+5,outlen);
+ return true;
+ }
+ else PRINTF(L_SYS_MAP,"auxsrv: bad footer in map%02x response",map);
+ }
+ else PRINTF(L_SYS_MAP,"auxsrv: bad length in map%02x response (got=%d want=%d)",map,l-1,outlen);
+ }
+ else PRINTF(L_SYS_MAP,"auxsrv: map%02x not successfull (unsupported?)",map);
+ }
+ else PRINTF(L_SYS_MAP,"auxsrv: bad response to map%02x",map);
+ }
+ else PRINTF(L_SYS_MAP,"auxsrv: map%02x read failed",map);
+ }
+ else PRINTF(L_SYS_MAP,"auxsrv: map%02x write failed",map);
+ so.Disconnect();
+ return false;
+}
+*/
+
+// -- cMap0101 ----------------------------------------------------------------
+
+#include "nagra2-map57.c"
+
+class cMap0101 : public cMapCore {
+private:
+ static const unsigned char primes[];
+ unsigned char residues[53];
+// cAuxSrv aux;
+ cN2Map57 map57;
+protected:
+ void DoMap(int f, unsigned char *data=0, int l=0);
+public:
+ cMap0101(void);
+ };
+
+const unsigned char cMap0101::primes[] = {
+ 0x03,0x05,0x07,0x0B,0x0D,0x11,0x13,0x17,0x1D,0x1F,0x25,0x29,0x2B,0x2F,0x35,0x3B,
+ 0x3D,0x43,0x47,0x49,0x4F,0x53,0x59,0x61,0x65,0x67,0x6B,0x6D,0x71,0x7F,0x83,0x89,
+ 0x8B,0x95,0x97,0x9D,0xA3,0xA7,0xAD,0xB3,0xB5,0xBF,0xC1,0xC5,0xC7,0xD3,0xDF,0xE3,
+ 0xE5,0xE9,0xEF,0xF1,0xFB
+ };
+
+cMap0101::cMap0101(void)
+{
+}
+
+void cMap0101::DoMap(int f, unsigned char *data, int l)
+{
+ PRINTF(L_SYS_MAP,"0101: calling function %02X",f);
+ switch(f) {
+ case 0x3b:
+ MakeJ();
+ BN_zero(R);
+ BN_set_bit(R,132); // or 66*wordsize ?
+ BN_mod(H,R,D,ctx);
+ for(int i=0; i<4; i++) MonMul(H,H,H);
+ MonMul(B,A,H);
+ break;
+ case 0x4d:
+ for(int i=0; i<53; i++) residues[i]=BN_mod_word(A,primes[i]);
+ break;
+ case 0x4e:
+ {
+ bool isPrime;
+ do {
+ BN_add_word(A,2);
+ isPrime=true;
+ for(int i=0; i<53; i++) {
+ residues[i]+=2;
+ residues[i]%=primes[i];
+ if(residues[i]==0) isPrime=false;
+ }
+ } while(!isPrime);
+ break;
+ }
+ case 0x57:
+ map57.Map57(data);
+ //aux.Map(0x57,data,l,l);
+ break;
+ default:
+ if(!cMapCore::DoMap(f,data,l))
+ PRINTF(L_SYS_MAP,"0101: unsupported call %02x",f);
+ break;
+ }
+}
+
+// -- cN2Prov0101 ----------------------------------------------------------------
+
+class cN2Prov0101 : public cN2Prov, public cN2Emu, private cMap0101 {
+protected:
+ virtual bool Algo(int algo, const unsigned char *hd, unsigned char *hw);
+ virtual void Stepper(void);
+public:
+ cN2Prov0101(int Id, int Flags):cN2Prov(Id,Flags) {}
+ virtual int ProcessBx(unsigned char *data, int len, int pos);
+ };
+
+static cN2ProvLinkReg<cN2Prov0101,0x0101,N2FLAG_MECM|N2FLAG_Bx> staticPL0101;
+
+bool cN2Prov0101::Algo(int algo, const unsigned char *hd, unsigned char *hw)
+{
+ if(algo==0x40) {
+ memcpy(hw,hd,3);
+ ExpandInput(hw);
+ hw[0x18]|=1; hw[0x40]|=1;
+ SetWordSize(2);
+ ImportReg(IMPORT_A,hw,3);
+ ImportReg(IMPORT_D,hw+24);
+ DoMap(0x3b);
+ ExportReg(EXPORT_C,hw);
+ ImportReg(IMPORT_A,hw+40,3);
+ ImportReg(IMPORT_D,hw+64);
+ DoMap(0x3b);
+ ExportReg(EXPORT_C,hw+40);
+ DoMap(0x43);
+ DoMap(0x44,hw);
+ DoMap(0x44,hw+64);
+ hw[0]=hw[64+4]; // ctx.h3&0xFF
+ hw[1]=hw[64+5]; //(ctx.h3>>8)&0xFF
+ memset(&hw[2],0,128-2);
+ return true;
+ }
+ else if(algo==0x60) { // map 4D/4E/57
+ memcpy(hw,hd,5);
+ ExpandInput(hw);
+ hw[127]|=0x80; hw[0]|=0x01;
+ SetWordSize(16);
+ ImportReg(IMPORT_A,hw);
+ DoMap(0x4d);
+ DoMap(0x4e);
+ ExportReg(EXPORT_A,hw,16,true);
+ ImportReg(IMPORT_A,hw);
+ DoMap(0x4e);
+ ExportReg(EXPORT_A,hw);
+ DoMap(0x57,hw,128);
+ return true;
+ }
+
+ PRINTF(L_SYS_ECM,"%04X: unknown MECM algo %02x",id,algo);
+ return false;
+}
+
+int cN2Prov0101::ProcessBx(unsigned char *data, int len, int pos)
+{
+ if(Init(0x0101,102)) {
+ SetMem(0x80,data,len);
+ SetPc(0x80+pos);
+ SetSp(0x0FFF,0x0FF0);
+ ClearBreakpoints();
+ AddBreakpoint(0x0000);
+ AddBreakpoint(0x9569);
+ AddBreakpoint(0xA822); // map handler
+ while(!Run(1000)) {
+ if(GetPc()==0x9569) {
+ GetMem(0x80,data,len);
+ return max((int)a,6);
+ }
+ if(GetPc()==0x0000)
+ break;
+ if(GetPc()==0xA822) {
+ int size=wordsize<<3;
+ unsigned char tmp[size];
+ unsigned short addr=(Get(0x44)<<8)+Get(0x45);
+ switch(a) {
+ case SETSIZE:
+ SetWordSize(Get(0x48)); break;
+ case IMPORT_A ... IMPORT_D:
+ GetMem(addr,tmp,size,0); ImportReg(a,tmp); break;
+ case EXPORT_A ... EXPORT_D:
+ ExportReg(a,tmp); SetMem(addr,tmp,size,0); break;
+ case 0x0F:
+ {
+ unsigned char tmp2[size];
+ GetMem(addr,tmp2,size,0);
+ ExportReg(EXPORT_A,tmp);
+ ImportReg(IMPORT_A,tmp2);
+ SetMem(addr,tmp,size,0);
+ break;
+ }
+ default:
+ PRINTF(L_SYS_EMM,"%04X: unrecognized map call %02x",id,a);
+ return -1;
+ }
+ PopCr(); PopPc();
+ }
+ }
+ }
+ return -1;
+}
+
+void cN2Prov0101::Stepper(void)
+{
+ unsigned short pc=GetPc();
+ if(pc>=0x93 && pc<=0xE0) { // pc in EMM data
+ unsigned char op=Get(pc);
+ if((op&0xF0)==0x00) { // current opcode BRCLR/BRSET
+ int fake=0; // 1=branch -1=non-branch
+ if(Get(pc+3)==0x81) // next opcode == RTS
+ fake=1; // fake branch
+ else {
+ unsigned char off=Get(pc+2);
+ unsigned short target=pc+3+off;
+ if(off&0x80) target-=0x100;
+ if(Get(target)==0x81) // branch target == RTS
+ fake=-1; // fake non-branch
+ }
+ if(fake) {
+ unsigned short ea=Get(pc+1);
+ unsigned char val=Get(dr,ea);
+ int bit=1<<((op&0xF)>>1);
+ // set/clr bit according to fake-mode and opcode
+ if((fake>0 && (op&0x01)) || (fake<0 && !(op&0x01))) {
+ if(val&bit) loglb->Printf("*");
+ val&=~bit;
+ }
+ else {
+ if(!(val&bit)) loglb->Printf("*");
+ val|=bit;
+ }
+ Set(dr,ea,val);
+ }
+ }
+ }
+}
+
+// -- cN2Prov0901 --------------------------------------------------------------
+
+class cN2Prov0901 : public cN2Prov0101 {
+public:
+ cN2Prov0901(int Id, int Flags):cN2Prov0101(Id,Flags) {}
+ virtual int ProcessBx(unsigned char *data, int len, int pos);
+ };
+
+static cN2ProvLinkReg<cN2Prov0901,0x0901,N2FLAG_MECM|N2FLAG_Bx> staticPL0901;
+
+int cN2Prov0901::ProcessBx(unsigned char *data, int len, int pos)
+{
+ if(Init(0x0801,102)) {
+ return cN2Prov0101::ProcessBx(data,len,pos);
+ }
+ return -1;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+// -- cMap0501 -----------------------------------------------------------------
+
+class cMap0501 : public cMapCore {
+private:
+protected:
+ void DoMap(int f, unsigned char *data=0, int l=0);
+public:
+ cMap0501(void);
+ };
+
+cMap0501::cMap0501(void)
+{
+}
+
+void cMap0501::DoMap(int f, unsigned char *data, int l)
+{
+ PRINTF(L_SYS_MAP,"0501: calling function %02X",f);
+ switch(f) {
+ case 0x37:
+ l=(l?l:wordsize)<<3;
+ H.GetLE(data,l);
+ MonMul(B,H,A);
+ break;
+ case 0x3a:
+ MakeJ();
+ BN_zero(R);
+ BN_set_bit(R,68*wordsize);
+ BN_mod(H,R,D,ctx);
+ for(int i=0; i<4; i++) MonMul(H,H,H);
+ MonMul(B,A,H);
+ MonMul(B,A,B);
+ break;
+ default:
+ if(!cMapCore::DoMap(f,data,l))
+ PRINTF(L_SYS_MAP,"0501: unsupported call %02x",f);
+ break;
+ }
+}
+
+// -- cN2Prov0501 --------------------------------------------------------------
+
+class cN2Prov0501 : public cN2Prov, private cMap0501 {
+protected:
+ virtual bool Algo(int algo, const unsigned char *hd, unsigned char *hw);
+ virtual bool NeedsCwSwap(void) { return true; }
+public:
+ cN2Prov0501(int Id, int Flags):cN2Prov(Id,Flags) {}
+ };
+
+static cN2ProvLinkReg<cN2Prov0501,0x0501,N2FLAG_MECM|N2FLAG_INV> staticPL0501;
+
+bool cN2Prov0501::Algo(int algo, const unsigned char *hd, unsigned char *hw)
+{
+ if(algo==0x60) {
+ hw[0]=hd[0];
+ hw[1]=hd[1];
+ hw[2]=hd[2]&0xF8;
+ ExpandInput(hw);
+ hw[63]|=0x80;
+ hw[95]=hw[127]=hw[95]&0x7F;
+ SetWordSize(4);
+ ImportReg(IMPORT_J,hw+0x18);
+ ImportReg(IMPORT_D,hw+0x20);
+ ImportReg(IMPORT_A,hw+0x60);
+ DoMap(0x37,hw+0x40);
+ ExportReg(EXPORT_C,hw);
+ DoMap(0x3a);
+ ExportReg(EXPORT_C,hw+0x20);
+ DoMap(0x43);
+ DoMap(0x44,hw);
+ hw[0]&=7;
+ ExportReg(EXPORT_B,hw+3);
+ memset(hw+3+0x20,0,128-(3+0x20));
+ return true;
+ }
+
+ PRINTF(L_SYS_ECM,"0501: unknown MECM algo %02x",algo);
+ return false;
+}
+
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+// -- cN2Prov1101 ----------------------------------------------------------------
+
+class cN2Prov1101 : public cN2Prov {
+protected:
+ virtual bool NeedsCwSwap(void) { return true; }
+public:
+ cN2Prov1101(int Id, int Flags):cN2Prov(Id,Flags) {}
+ };
+
+static cN2ProvLinkReg<cN2Prov1101,0x1101,N2FLAG_INV> staticPL1101;
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+// -- cN2Prov4101 ----------------------------------------------------------------
+
+class cN2Prov4101 : public cN2Prov {
+public:
+ cN2Prov4101(int Id, int Flags):cN2Prov(Id,Flags) {}
+ virtual bool PostProcAU(int id, unsigned char *data);
+ };
+
+static cN2ProvLinkReg<cN2Prov4101,0x4101,N2FLAG_POSTAU> staticPL4101;
+
+bool cN2Prov4101::PostProcAU(int id, unsigned char *data)
+{
+ if(data[1]==0x01) {
+ cPlainKey *pk;
+ if(!(pk=keys.FindKey('N',id,MBC(N2_MAGIC,0x30),16))) {
+ PRINTF(L_SYS_EMM,"missing %04x NN 30 3DES key (16 bytes)",id);
+ return false;
+ }
+ unsigned char dkey[16];
+ pk->Get(dkey);
+ DES_key_schedule ks1, ks2;
+ DES_key_sched((DES_cblock *)&dkey[0],&ks1);
+ DES_key_sched((DES_cblock *)&dkey[8],&ks2);
+ DES_ecb2_encrypt(DES_CAST(&data[7]),DES_CAST(&data[7]),&ks1,&ks2,DES_DECRYPT);
+ DES_ecb2_encrypt(DES_CAST(&data[7+8]),DES_CAST(&data[7+8]),&ks1,&ks2,DES_DECRYPT);
+ }
+ return true;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+// -- cN2Map57 ----------------------------------------------------------------
+
+class cN2Map57 {
+private:
+ void mod_add(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4);
+ void bn_cmplx1(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4, BIGNUM *arg5);
+ void bn_cmplx1a(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4, BIGNUM *arg5);
+ void mod_sub(void);
+ void bn_func1(BIGNUM *arg0);
+ void bn_func2(int arg0);
+ void bn_func3(int arg0);
+ void bn_cmplx7(void);
+ void bn_cmplx2(BIGNUM *var1, BIGNUM *var2, BIGNUM *var3, BIGNUM *var4, BIGNUM *var5, BIGNUM *var6);
+ BIGNUM *bn_glb0, *bn_glb1, *bn_glb3, *bn_glb5, *bn_glb6, *bn_glb7;
+ BIGNUM *bn_glb_a, *bn_glb_b, *bn_glb_c, *bn_glb_d, *bn_glb_e, *bn_glb_f, *bn_glb_g;
+ BIGNUM *bn_glb_h, *bn_glb_i, *bn_glb_j, *bn_glb_k, *bn_glb_l, *bn_glb_m;
+ BIGNUM *glb2pow128, *mask128, *glb2pow64, *mask64;
+ BN_CTX *t1;
+public:
+ void Map57(unsigned char *data);
+ };
+
+void cN2Map57::mod_add(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4)
+{
+ BN_add(arg1, arg2, arg3);
+ if(BN_cmp(arg1, arg4) >= 0) {
+ BN_sub(arg1, arg1, arg4);
+ }
+ BN_mask_bits(arg1, 128);
+}
+
+void cN2Map57::bn_cmplx1(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4, BIGNUM *arg5)
+{
+ int j;
+ BIGNUM *var44, *var64, *var84, *vara4;
+ var44 = BN_new();
+ var64 = BN_new();
+ var84 = BN_new();
+ vara4 = BN_new();
+ BN_copy(var44, arg2);
+ BN_copy(var64, arg3);
+ BN_clear(vara4);
+ for(j=0; j<2; j++) {
+ BN_copy(var84, var64);
+ BN_mask_bits(var84, 64);
+ BN_rshift(var64, var64, 64);
+ BN_mul(var84, var84, var44, t1);
+ BN_add(vara4, vara4, var84);
+ BN_copy(var84, vara4);
+ BN_mask_bits(var84, 128);
+ BN_mul(var84, vara4, arg4, t1);
+ BN_mask_bits(var84, 64);
+ BN_mul(var84, var84, arg5, t1);
+ BN_add(vara4, vara4, var84);
+ BN_rshift(vara4, vara4, 64);
+ if(BN_cmp(vara4, arg5) >= 0) {
+ BN_sub(vara4, vara4, arg5);
+ }
+ BN_mask_bits(vara4, 128);
+ }
+ BN_copy(arg1, vara4);
+ BN_free(var44);
+ BN_free(var64);
+ BN_free(var84);
+ BN_free(vara4);
+}
+
+void cN2Map57::bn_cmplx1a(BIGNUM *arg1, BIGNUM *arg2, BIGNUM *arg3, BIGNUM *arg4, BIGNUM *arg5)
+{
+ int j;
+ BIGNUM *var44, *var64, *var84, *vara4;
+ var44 = BN_new();
+ var64 = BN_new();
+ var84 = BN_new();
+ vara4 = BN_new();
+ BN_copy(var44, arg2);
+ BN_copy(var64, arg3);
+ BN_clear(vara4);
+ for(j=0; j<2; j++) {
+ BN_copy(var84, var64);
+ BN_mask_bits(var84, 64);
+ BN_rshift(var64, var64, 64);
+ BN_mul(var84, var84, var44, t1);
+ BN_add(vara4, vara4, var84);
+ BN_copy(var84, vara4);
+ BN_mask_bits(var84, 128);
+ BN_mul(var84, vara4, arg4, t1);
+ BN_mask_bits(var84, 64);
+ BN_mul(var84, var84, arg5, t1);
+ BN_add(vara4, vara4, var84);
+ BN_rshift(vara4, vara4, 64);
+ if(j==0 && BN_cmp(vara4, arg5) >= 0) {
+ BN_sub(vara4, vara4, arg5);
+ }
+ BN_mask_bits(vara4, 128);
+ }
+ BN_copy(arg1, vara4);
+ BN_free(var44);
+ BN_free(var64);
+ BN_free(var84);
+ BN_free(vara4);
+}
+
+//uses 3, 1, glb2pow128
+//sets 1, 0 (unused)
+void cN2Map57::mod_sub()
+{
+ BN_copy(bn_glb0, bn_glb3);
+ BN_mod_sub(bn_glb1, bn_glb3, bn_glb1, glb2pow128, t1);
+ BN_mask_bits(bn_glb1, 128);
+}
+
+//uses 1, 3, 6
+//sets 1, 0 (unused), 7(unused)
+void cN2Map57::bn_func1(BIGNUM *arg0)
+{
+ BIGNUM *var30 = BN_new();
+ BIGNUM *var50 = BN_new();
+ BN_copy(var30,arg0);
+ BN_mask_bits(var30, 8);
+ unsigned int x = BN_get_word(var30);
+ BN_copy(var30,arg0);
+ if( x != 0) {
+ BN_clear(var50);
+ BN_set_word(var50, 2);
+ BN_sub(var30, var30, var50);
+ } else {
+ BN_clear(var50);
+ BN_set_word(var50, 0xfe);
+ BN_add(var30, var30, var50);
+ }
+ BN_copy(bn_glb7, bn_glb1);
+ if(BN_is_zero(arg0)) {
+ BN_clear(bn_glb7);
+ BN_set_word(bn_glb7, 1);
+ BN_clear(bn_glb0);
+
+ mod_add(bn_glb1, bn_glb7, bn_glb0, bn_glb3);
+ BN_free(var30);
+ BN_free(var50);
+ return;
+ } else {
+ int msb = BN_num_bits(var30) -1;
+ while (msb > 0) {
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+ msb--;
+ if(BN_is_bit_set(var30, msb)) {
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ }
+ }
+ BN_clear(bn_glb7);
+ BN_set_word(bn_glb7, 1);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ BN_clear(bn_glb0);
+ }
+ BN_free(var30);
+ BN_free(var50);
+}
+
+//uses 3, 6, a, b, c, l, glb2pow128
+//sets 0, 1, 5, 7, a, b, c, f, g
+void cN2Map57::bn_func2(int arg0)
+{
+ BN_copy(bn_glb1, bn_glb_b);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+ BN_copy(bn_glb7, bn_glb1);
+ BN_copy(bn_glb5, bn_glb_c);
+ BN_mask_bits(bn_glb1, 128);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_g, bn_glb1);
+ BN_copy(bn_glb1, bn_glb7);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+ BN_copy(bn_glb7, bn_glb_a);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ mod_sub();
+ BN_copy(bn_glb_f, bn_glb1);
+ BN_copy(bn_glb1, bn_glb7);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+ BN_copy(bn_glb7, bn_glb1);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+ BN_copy(bn_glb7, bn_glb1);
+ BN_copy(bn_glb1, bn_glb_c);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+ BN_copy(bn_glb5, bn_glb_l);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+ BN_copy(bn_glb7, bn_glb1);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+ BN_copy(bn_glb0, bn_glb_f);
+
+ mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+ mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+ if(arg0 == 0) {
+ BN_copy(bn_glb_a, bn_glb1);
+ } else {
+ BN_copy(bn_glb_f, bn_glb1);
+ }
+
+ mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ BN_copy(bn_glb7, bn_glb1);
+ BN_copy(bn_glb1, bn_glb_b);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+ mod_sub();
+ if(arg0 == 0) {
+ BN_copy(bn_glb_b, bn_glb1);
+ BN_copy(bn_glb_c, bn_glb_g);
+ } else {
+ BN_copy(bn_glb_f, bn_glb1);
+ BN_copy(bn_glb_f, bn_glb_g);
+ }
+}
+
+//uses 3, 6, a, b, c, d, e, k
+//sets 0, 1, 5, 7, a, b, c, f, g, h, i, j
+void cN2Map57::bn_func3(int arg0)
+{
+ BN_copy(bn_glb1, bn_glb_c);
+ BN_copy(bn_glb7, bn_glb1);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+ bn_cmplx1(bn_glb0, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_f, bn_glb0);
+ BN_copy(bn_glb5, bn_glb_d);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb7, bn_glb1);
+ mod_sub();
+ BN_copy(bn_glb0, bn_glb_a);
+
+ mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+ BN_copy(bn_glb_g, bn_glb1);
+ BN_copy(bn_glb5, bn_glb_c);
+
+ bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ if(arg0 == 0) {
+ BN_copy(bn_glb_c, bn_glb0);
+ } else {
+ BN_copy(bn_glb_g, bn_glb0);
+ }
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_h, bn_glb1);
+ BN_copy(bn_glb0, bn_glb_a);
+
+ mod_add(bn_glb0, bn_glb0, bn_glb7, bn_glb3);
+ BN_copy(bn_glb7, bn_glb0);
+
+ //NOTE: don't 'mod' bn_glb1, but DO 'mod' glb_i
+ bn_cmplx1(bn_glb7, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ bn_cmplx1a(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_i, bn_glb7);
+ BN_copy(bn_glb1, bn_glb_e);
+ BN_copy(bn_glb5, bn_glb_f);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_f, bn_glb1);
+ mod_sub();
+ BN_copy(bn_glb0, bn_glb_b);
+
+ mod_add(bn_glb1, bn_glb0, bn_glb1, bn_glb3);
+ BN_copy(bn_glb_j, bn_glb1);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+ BN_copy(bn_glb0, bn_glb1);
+ BN_copy(bn_glb1, bn_glb7);
+ BN_copy(bn_glb7, bn_glb0);
+ mod_sub();
+
+ mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+ if(arg0 == 0) {
+ BN_copy(bn_glb_a, bn_glb1);
+ } else {
+ BN_copy(bn_glb_f, bn_glb1);
+ }
+
+ mod_add(bn_glb1, bn_glb1, bn_glb1, bn_glb3);
+ mod_sub();
+ BN_copy(bn_glb7, bn_glb_i);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+ BN_copy(bn_glb5, bn_glb_j);
+
+ bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb1, bn_glb_f);
+ BN_copy(bn_glb_f, bn_glb0);
+ BN_copy(bn_glb7, bn_glb_b);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+ BN_copy(bn_glb7, bn_glb1);
+ BN_copy(bn_glb1, bn_glb_g);
+ BN_copy(bn_glb5, bn_glb_h);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ mod_sub();
+ BN_copy(bn_glb7, bn_glb_f);
+
+ mod_add(bn_glb1, bn_glb1, bn_glb7, bn_glb3);
+ BN_copy(bn_glb5, bn_glb_k);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ if(arg0 == 0) {
+ BN_copy(bn_glb_b, bn_glb1);
+ } else {
+ BN_copy(bn_glb_f, bn_glb1);
+ }
+}
+
+//uses c, d, e, m
+//sets 0, a, b, c
+void cN2Map57::bn_cmplx7()
+{
+ BIGNUM *var1;
+ var1 = BN_new();
+ BN_copy(bn_glb0, bn_glb_c);
+ if(BN_is_zero(bn_glb_c)) {
+ BN_copy(bn_glb_a, bn_glb_d);
+ BN_copy(bn_glb_b, bn_glb_e);
+ BN_copy(bn_glb_c, bn_glb_m);
+ bn_func3(1);
+ } else {
+ BN_clear(var1);
+ BN_set_word(var1, 0xFFFFFFFF);
+ BN_mask_bits(bn_glb_a, 32);
+ BN_lshift(var1, bn_glb_m, 0x20);
+ BN_add(bn_glb_a, bn_glb_a, var1);
+ BN_mask_bits(bn_glb_a, 128);
+ bn_func3(0);
+ }
+ BN_free(var1);
+}
+
+void cN2Map57::bn_cmplx2(BIGNUM *var1, BIGNUM *var2, BIGNUM *var3, BIGNUM *var4, BIGNUM *var5, BIGNUM *var6)
+{
+ BIGNUM *var48;
+ int len = BN_num_bits(var6);
+ int i;
+ if(len < 2)
+ return;
+
+ if(BN_is_zero(var2) && BN_is_zero(var3) && BN_is_zero(var4))
+ return;
+ var48 = BN_new();
+ BN_copy(bn_glb3, var1);
+
+ BN_copy(bn_glb6, bn_glb3);
+ BN_set_bit(bn_glb6, 0);
+ BN_sub(bn_glb6, glb2pow128, bn_glb6);
+ BN_mod_inverse(bn_glb6, bn_glb6, glb2pow64, t1);
+ BN_clear(bn_glb0);
+ //
+ if(! BN_is_zero(bn_glb3)) {
+ BN_clear(bn_glb1);
+ BN_set_word(bn_glb1, 2);
+ BN_clear(bn_glb_k);
+ BN_set_word(bn_glb_k, 0x88);
+ BN_mod_exp(bn_glb1, bn_glb1, bn_glb_k, bn_glb3, t1);
+ }
+ //
+ for(i=0; i < 4; i++) {
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+ }
+ //
+ BN_clear(bn_glb7);
+ BN_set_word(bn_glb7, 1);
+ BN_add(bn_glb0, bn_glb3, bn_glb7);
+ BN_copy(bn_glb_k, bn_glb0);
+ BN_rshift(bn_glb_k, bn_glb_k, 1);
+ BN_copy(bn_glb7, bn_glb1);
+ BN_copy(bn_glb5, bn_glb_k);
+ BN_mask_bits(bn_glb5, 128);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_k, bn_glb1);
+
+ BN_copy(bn_glb1, var5);
+ BN_mask_bits(bn_glb1, 128);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_l, bn_glb1);
+ BN_copy(bn_glb1, bn_glb7);
+ BN_clear(bn_glb5);
+ BN_set_word(bn_glb5, 1);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_c, bn_glb1);
+ BN_copy(bn_glb_m, bn_glb1);
+ BN_copy(bn_glb1, bn_glb7);
+
+ BN_copy(bn_glb5, var2);
+ BN_mask_bits(bn_glb5, 128);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_a, bn_glb1);
+ BN_copy(bn_glb1, bn_glb7);
+
+ BN_copy(bn_glb5, var3);
+ BN_mask_bits(bn_glb5, 128);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_b, bn_glb1);
+ BN_copy(bn_glb_d, bn_glb_a);
+ BN_copy(bn_glb_e, bn_glb_b);
+
+ int x = len -1;
+ while(x > 0) {
+ x--;
+ bn_func2(0);
+ if(BN_is_bit_set(var6, x)) {
+ bn_cmplx7();
+ }
+ }
+
+ BN_copy(bn_glb1, bn_glb_c);
+ BN_mask_bits(bn_glb1, 128);
+ BN_copy(bn_glb7, bn_glb1);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb1, bn_glb6, bn_glb3);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ BN_clear(bn_glb7);
+ BN_set_word(bn_glb7, 1);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb7, bn_glb6, bn_glb3);
+ BN_copy(bn_glb0, bn_glb1);
+ BN_clear(bn_glb7);
+ BN_set_word(bn_glb7, 1);
+ BN_copy(bn_glb1, bn_glb0);
+ BN_clear(bn_glb0);
+ bn_func1(var1);
+ BN_copy(bn_glb5, bn_glb_b);
+ BN_mask_bits(bn_glb5, 128);
+
+ bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+ BN_copy(bn_glb7, bn_glb0);
+ BN_copy(bn_glb5, bn_glb_c);
+ BN_mask_bits(bn_glb5, 128);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+ BN_copy(bn_glb5, bn_glb_a);
+ BN_mask_bits(bn_glb5, 128);
+
+ bn_cmplx1(bn_glb1, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+
+ BN_clear(bn_glb5);
+ BN_set_word(bn_glb5, 1);
+
+ bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_a, bn_glb0);
+ BN_copy(bn_glb1, bn_glb7);
+
+ BN_clear(bn_glb5);
+ BN_set_word(bn_glb5, 1);
+
+ bn_cmplx1(bn_glb0, bn_glb1, bn_glb5, bn_glb6, bn_glb3);
+ BN_copy(bn_glb_b, bn_glb0);
+ BN_free(var48);
+}
+
+void cN2Map57::Map57(unsigned char *data)
+{
+ BIGNUM *var38, *var58, *var78, *var98, *varb8, *vard8;
+ BN_CTX *t;
+ unsigned char tmpdata[256];
+ unsigned char res[256];
+
+ t = BN_CTX_new();
+ t1 = BN_CTX_new();
+ BN_CTX_init(t);
+
+ glb2pow128 = BN_new();
+ BN_clear(glb2pow128);
+ BN_set_bit(glb2pow128, 128);
+ mask128 = BN_new();
+ BN_hex2bn(&mask128, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
+
+ glb2pow64 = BN_new();
+ BN_clear(glb2pow64);
+ BN_set_bit(glb2pow64, 64);
+ mask64 = BN_new();
+ BN_hex2bn(&mask64, "FFFFFFFFFFFFFFFF");
+
+ bn_glb0=BN_new(); BN_clear(bn_glb0);
+ bn_glb1=BN_new(); BN_clear(bn_glb1);
+ bn_glb3=BN_new(); BN_clear(bn_glb3);
+ bn_glb5=BN_new(); BN_clear(bn_glb5);
+ bn_glb6=BN_new(); BN_clear(bn_glb6);
+ bn_glb7=BN_new(); BN_clear(bn_glb7);
+
+ bn_glb_a=BN_new(); BN_clear(bn_glb_a);
+ bn_glb_b=BN_new(); BN_clear(bn_glb_b);
+ bn_glb_c=BN_new(); BN_clear(bn_glb_c);
+ bn_glb_d=BN_new(); BN_clear(bn_glb_d);
+ bn_glb_e=BN_new(); BN_clear(bn_glb_e);
+ bn_glb_f=BN_new(); BN_clear(bn_glb_f);
+ bn_glb_g=BN_new(); BN_clear(bn_glb_g);
+ bn_glb_h=BN_new(); BN_clear(bn_glb_h);
+ bn_glb_i=BN_new(); BN_clear(bn_glb_i);
+ bn_glb_j=BN_new(); BN_clear(bn_glb_j);
+ bn_glb_k=BN_new(); BN_clear(bn_glb_k);
+ bn_glb_l=BN_new(); BN_clear(bn_glb_l);
+ bn_glb_m=BN_new(); BN_clear(bn_glb_m);
+
+ var38=BN_new(); BN_clear(var38);
+ var58=BN_new(); BN_clear(var58);
+ var78=BN_new(); BN_clear(var78);
+ var98=BN_new(); BN_clear(var98);
+ varb8=BN_new(); BN_clear(varb8);
+ vard8=BN_new(); BN_clear(vard8);
+
+ memcpy(tmpdata, data, 0x80);
+ RotateBytes(tmpdata, 0x80);
+ BN_bin2bn(tmpdata, 16, var78);
+ BN_bin2bn(tmpdata+0x10, 16, varb8);
+ BN_bin2bn(tmpdata+0x20, 16, var98);
+ BN_bin2bn(tmpdata+0x40, 16, vard8);
+ BN_bin2bn(tmpdata+0x60, 16, var38);
+ BN_bin2bn(tmpdata+0x70, 16, var58);
+
+ bn_cmplx2(varb8, var58, vard8, var38, var78, var98);
+
+ memset(res, 0, 0x80);
+ unsigned int *dest = (unsigned int *)res, *src = (unsigned int *)data;
+ *dest++ = src[0x03];
+ *dest++ = src[0x02];
+ *dest++ = src[0x01];
+ *dest++ = src[0x00];
+ *dest++ = src[0x07];
+ *dest++ = src[0x06];
+ *dest++ = src[0x05];
+ *dest++ = src[0x04];
+
+ memset(tmpdata, 0, 0x20);
+ int len = BN_bn2bin(bn_glb_a, tmpdata);
+ if(len) {
+ RotateBytes(tmpdata, len);
+ }
+ src = (unsigned int *)tmpdata;
+ *dest++ = src[0x03];
+ *dest++ = src[0x02];
+ *dest++ = src[0x01];
+ *dest++ = src[0x00];
+
+ memset(tmpdata, 0, 0x20);
+ len = BN_bn2bin(bn_glb_m, tmpdata);
+ if(len) {
+ RotateBytes(tmpdata, len);
+ }
+ *dest = src[0x03];
+ dest+=4;
+
+ memset(tmpdata, 0, 0x20);
+ len = BN_bn2bin(bn_glb_b, tmpdata);
+ if(len) {
+ RotateBytes(tmpdata, len);
+ }
+ *dest++ = src[0x03];
+ *dest++ = src[0x02];
+ *dest++ = src[0x01];
+ *dest++ = src[0x00];
+
+ dest+=4;
+ src = (unsigned int *)(data+0x60);
+ *dest++ = src[0x03];
+ *dest++ = src[0x02];
+ *dest++ = src[0x01];
+ *dest++ = src[0x00];
+ *dest++ = src[0x07];
+ *dest++ = src[0x06];
+ *dest++ = src[0x05];
+ *dest++ = src[0x04];
+
+ *(unsigned int *)(data + (8<<2))= *(unsigned int *)(res + (11<<2));
+ *(unsigned int *)(data + (9<<2))= *(unsigned int *)(res + (10<<2));
+ *(unsigned int *)(data + (10<<2))= *(unsigned int *)(res + (9<<2));
+ *(unsigned int *)(data + (11<<2))= *(unsigned int *)(res + (8<<2));
+ *(unsigned int *)(data + (12<<2))= *(unsigned int *)(res + (12<<2));
+ *(unsigned int *)(data + (13<<2))= *(unsigned int *)(res + (13<<2));
+ *(unsigned int *)(data + (14<<2))= *(unsigned int *)(res + (14<<2));
+ *(unsigned int *)(data + (15<<2))= *(unsigned int *)(res + (15<<2));
+ *(unsigned int *)(data + (16<<2))= *(unsigned int *)(res + (19<<2));
+ *(unsigned int *)(data + (17<<2))= *(unsigned int *)(res + (18<<2));
+ *(unsigned int *)(data + (18<<2))= *(unsigned int *)(res + (17<<2));
+ *(unsigned int *)(data + (19<<2))= *(unsigned int *)(res + (16<<2));
+ *(unsigned int *)(data + (20<<2))= *(unsigned int *)(res + (20<<2));
+ *(unsigned int *)(data + (21<<2))= *(unsigned int *)(res + (21<<2));
+ *(unsigned int *)(data + (22<<2))= *(unsigned int *)(res + (22<<2));
+ *(unsigned int *)(data + (23<<2))= *(unsigned int *)(res + (23<<2));
+
+ BN_free(glb2pow128);
+ BN_free(mask128);
+ BN_free(glb2pow64);
+ BN_free(mask64);
+
+ BN_free(bn_glb0);
+ BN_free(bn_glb1);
+ BN_free(bn_glb3);
+ BN_free(bn_glb5);
+ BN_free(bn_glb6);
+ BN_free(bn_glb7);
+
+ BN_free(bn_glb_a);
+ BN_free(bn_glb_b);
+ BN_free(bn_glb_c);
+ BN_free(bn_glb_d);
+ BN_free(bn_glb_e);
+ BN_free(bn_glb_f);
+ BN_free(bn_glb_g);
+ BN_free(bn_glb_h);
+ BN_free(bn_glb_i);
+ BN_free(bn_glb_j);
+ BN_free(bn_glb_k);
+ BN_free(bn_glb_l);
+ BN_free(bn_glb_m);
+
+ BN_free(var38);
+ BN_free(var58);
+ BN_free(var78);
+ BN_free(var98);
+ BN_free(varb8);
+ BN_free(vard8);
+
+ BN_CTX_free(t);
+ BN_CTX_free(t1);
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "system.h"
+#include "misc.h"
+#include "opts.h"
+#include "network.h"
+#include "crypto.h"
+#include "helper.h"
+
+#include <openssl/des.h>
+#include <openssl/sha.h>
+#include "openssl-compat.h"
+
+#include "nagra.h"
+#include "cpu.h"
+#include "log-nagra.h"
+
+#define SYSTEM_NAME "Nagra2"
+#define SYSTEM_PRI -10
+
+// -- cN2Emu -------------------------------------------------------------------
+
+class cN2Emu : protected c6805 {
+private:
+ bool initDone;
+protected:
+ bool Init(int id, int romv);
+ virtual void Stepper(void) {}
+public:
+ cN2Emu(void);
+ virtual ~cN2Emu() {}
+ };
+
+cN2Emu::cN2Emu(void)
+{
+ initDone=false;
+}
+
+bool cN2Emu::Init(int id, int romv)
+{
+ if(!initDone) {
+ ResetMapper();
+ char buff[256];
+ snprintf(buff,sizeof(buff),"ROM%d.bin",romv);
+ // UROM 0x00:0x4000-0x7fff
+ if(!AddMapper(new cMapRom(0x4000,buff,0x00000),0x4000,0x4000,0x00)) return false;
+ // ROM00 0x00:0x8000-0xffff
+ if(!AddMapper(new cMapRom(0x8000,buff,0x04000),0x8000,0x8000,0x00)) return false;
+ // ROM01 0x01:0x8000-0xffff
+ if(!AddMapper(new cMapRom(0x8000,buff,0x0C000),0x8000,0x8000,0x01)) return false;
+ // ROM02 0x02:0x8000-0xbfff
+ if(!AddMapper(new cMapRom(0x8000,buff,0x14000),0x8000,0x4000,0x02)) return false;
+
+ snprintf(buff,sizeof(buff),"EEP%02X_%d.bin",(id>>8)&0xFF,romv);
+ // Eeprom00 0x00:0x3000-0x37ff OTP 0x80
+ if(!AddMapper(new cMapRom(0x3000,buff,0x0000),0x3000,0x0800,0x00)) return false;
+ //XXX if(!AddMapper(new cMapEeprom(0x3000,buff,128,0x0000),0x3000,0x0800,0x00)) return false;
+ // Eeprom80 0x80:0x8000-0xbfff
+ if(!AddMapper(new cMapRom(0x8000,buff,0x0800),0x8000,0x4000,0x80)) return false;
+ //XXX if(!AddMapper(new cMapEeprom(0x8000,buff, 0,0x0800),0x8000,0x4000,0x80)) return false;
+ initDone=true;
+ }
+ return true;
+}
+
+// -- cMapCore -----------------------------------------------------------------
+
+#define SETSIZE 0x02
+#define IMPORT_J 0x03
+#define IMPORT_A 0x04
+#define IMPORT_B 0x05
+#define IMPORT_C 0x06
+#define IMPORT_D 0x07
+#define EXPORT_A 0x0A
+#define EXPORT_B 0x0B
+#define EXPORT_C 0x0C
+#define EXPORT_D 0x0D
+
+class cMapCore {
+private:
+ cBN x, y, s, j;
+ SHA_CTX sctx;
+protected:
+ cBN A, B, C, D, J;
+ cBN H, R;
+ cBNctx ctx;
+ int wordsize;
+ //
+ void ImportReg(unsigned char reg, const unsigned char *data, int l=0);
+ void ExportReg(unsigned char reg, unsigned char *data, int l=0, bool BE=false);
+ void SetWordSize(int l) { wordsize=l; }
+ void MakeJ(void);
+ void MonMul(BIGNUM *o, BIGNUM *i1, BIGNUM *i2);
+ bool DoMap(int f, unsigned char *data=0, int l=0);
+public:
+ cMapCore(void);
+ };
+
+cMapCore::cMapCore(void)
+{
+ wordsize=4;
+}
+
+void cMapCore::ImportReg(unsigned char reg, const unsigned char *in, int l)
+{
+ l=(l?l:wordsize)<<3;
+ switch(reg) {
+ case IMPORT_J: J.GetLE(in,8); break;
+ case IMPORT_A: A.GetLE(in,l); break;
+ case IMPORT_B: B.GetLE(in,l); break;
+ case IMPORT_C: C.GetLE(in,l); break;
+ case IMPORT_D: D.GetLE(in,l); break;
+ default: PRINTF(L_GEN_DEBUG,"internal: nagramap import register not supported"); return;
+ }
+}
+
+void cMapCore::ExportReg(unsigned char reg, unsigned char *out, int l, bool BE)
+{
+ l=(l?l:wordsize)<<3;
+ cBN *ptr;
+ switch(reg) {
+ case EXPORT_A: ptr=&A; break;
+ case EXPORT_B: ptr=&B; break;
+ case EXPORT_C: ptr=&C; break;
+ case EXPORT_D: ptr=&D; break;
+ default: PRINTF(L_GEN_DEBUG,"internal: nagramap export register not supported"); return;
+ }
+ if(!BE) ptr->PutLE(out,l);
+ else ptr->Put(out,l);
+}
+
+void cMapCore::MakeJ(void)
+{
+#if OPENSSL_VERSION_NUMBER < 0x0090700fL
+#error BN_mod_inverse is probably buggy in your openssl version
+#endif
+ BN_zero(x);
+ BN_sub(J,x,D);
+ BN_set_bit(J,0);
+ BN_set_bit(x,64);
+ BN_mod_inverse(J,J,x,ctx);
+}
+
+void cMapCore::MonMul(BIGNUM *o, BIGNUM *i1, BIGNUM *i2)
+{
+ int words=(BN_num_bytes(i1)+7)>>3;
+ BN_zero(s);
+ for(int i=0; i<words; i++) {
+ BN_rshift(x,i1,i<<6);
+ BN_mask_bits(x,64);
+ BN_mul(x,x,i2,ctx);
+ BN_add(s,s,x);
+
+ BN_copy(x,s);
+ BN_mask_bits(x,64);
+ BN_mul(x,x,J,ctx);
+ if(i==(words-1)) {
+ BN_lshift(y,x,64);
+ BN_add(y,y,x);
+ // Low
+ BN_rshift(C,y,2);
+ BN_add(C,C,s);
+ BN_rshift(C,C,52);
+ BN_mask_bits(C,12);
+ }
+
+ BN_mask_bits(x,64);
+ BN_mul(x,x,D,ctx);
+ BN_add(s,s,x);
+ if(i==(words-1)) {
+ // High
+ BN_lshift(y,s,12);
+ BN_add(C,C,y);
+ BN_mask_bits(C,wordsize<<6);
+ }
+
+ BN_rshift(s,s,64);
+ if(BN_cmp(s,D)==1) {
+ BN_copy(x,s);
+ BN_sub(s,x,D);
+ }
+ }
+ BN_copy(o,s);
+}
+
+bool cMapCore::DoMap(int f, unsigned char *data, int l)
+{
+ switch(f) {
+ case 0x43: // init SHA1
+ SHA1_Init(&sctx);
+ break;
+ case 0x44: // add 64 bytes to SHA1 buffer
+ RotateBytes(data,64);
+ SHA1_Update(&sctx,data,64);
+ BYTE4_LE(data ,sctx.h4);
+ BYTE4_LE(data+4 ,sctx.h3);
+ BYTE4_LE(data+8 ,sctx.h2);
+ BYTE4_LE(data+12,sctx.h1);
+ BYTE4_LE(data+16,sctx.h0);
+ break;
+ case 0x45: // add wordsize bytes to SHA1 buffer and finalize SHA result
+ if(wordsize) {
+ if(wordsize>1) RotateBytes(data,wordsize);
+ SHA1_Update(&sctx,data,wordsize);
+ }
+ memset(data,0,64);
+ SHA1_Final(data+64,&sctx);
+ break;
+ default:
+ return false;
+ }
+ return true;
+}
+
+// -- cN2Prov ------------------------------------------------------------------
+
+class cN2Prov {
+private:
+ unsigned seed[5], cwkey[8];
+ bool keyValid;
+ cIDEA idea;
+protected:
+ int id, flags;
+ //
+ virtual bool Algo(int algo, const unsigned char *hd, unsigned char *hw) { return false; }
+ virtual bool NeedsCwSwap(void) { return false; }
+ void ExpandInput(unsigned char *hw);
+public:
+ cN2Prov(int Id, int Flags);
+ virtual ~cN2Prov() {}
+ bool MECM(unsigned char in15, int algo, unsigned char *cws);
+ void SwapCW(unsigned char *cw);
+ virtual int ProcessBx(unsigned char *data, int len, int pos) { return -1; }
+ virtual bool PostProcAU(int id, unsigned char *data) { return true; }
+ bool CanHandle(int Id) { return ((Id^id)&~0x107)==0; }
+ bool HasFlags(int Flags) { return (flags&Flags)==Flags; }
+ };
+
+cN2Prov::cN2Prov(int Id, int Flags)
+{
+ keyValid=false; id=Id; flags=Flags;
+}
+
+void cN2Prov::ExpandInput(unsigned char *hw)
+{
+ hw[0]^=(0xDE +(0xDE<<1)) & 0xFF;
+ hw[1]^=(hw[0]+(0xDE<<1)) & 0xFF;
+ for(int i=2; i<128; i++) hw[i]^=hw[i-2]+hw[i-1];
+ IdeaKS ks;
+ idea.SetEncKey((unsigned char *)"NagraVision S.A.",&ks);
+ unsigned char buf[8];
+ memset(buf,0,8);
+ for(int i=0; i<128; i+=8) {
+ xxor(buf,8,buf,&hw[i]);
+ idea.Encrypt(buf,8,buf,&ks,0);
+ xxor(buf,8,buf,&hw[i]);
+ memcpy(&hw[i],buf,8);
+ }
+}
+
+bool cN2Prov::MECM(unsigned char in15, int algo, unsigned char *cw)
+{
+ unsigned char hd[5], hw[128+64], buf[20];
+ hd[0]=in15&0x7F;
+ hd[1]=cw[14];
+ hd[2]=cw[15];
+ hd[3]=cw[6];
+ hd[4]=cw[7];
+
+ if(keyValid && !memcmp(seed,hd,5)) { // key cached
+ memcpy(buf,cwkey,8);
+ }
+ else { // key not cached
+ memset(hw,0,sizeof(hw));
+ if(!Algo(algo,hd,hw)) return false;
+ memcpy(&hw[128],hw,64);
+ RotateBytes(&hw[64],128);
+ SHA1(&hw[64],128,buf);
+ RotateBytes(buf,20);
+
+ memcpy(seed,hd,5);
+ memcpy(cwkey,buf,8);
+ keyValid=true;
+ }
+
+ memcpy(&buf[8],buf,8);
+ IdeaKS ks;
+ idea.SetEncKey(buf,&ks);
+ memcpy(&buf[0],&cw[8],6);
+ memcpy(&buf[6],&cw[0],6);
+ idea.Encrypt(&buf[4],8,&buf[4],&ks,0);
+ idea.Encrypt(buf,8,buf,&ks,0);
+
+ memcpy(&cw[ 0],&buf[6],3);
+ memcpy(&cw[ 4],&buf[9],3);
+ memcpy(&cw[ 8],&buf[0],3);
+ memcpy(&cw[12],&buf[3],3);
+ for(int i=0; i<16; i+=4) cw[i+3]=cw[i]+cw[i+1]+cw[i+2];
+ return true;
+}
+
+void cN2Prov::SwapCW(unsigned char *cw)
+{
+ if(NeedsCwSwap()) {
+ unsigned char tt[8];
+ memcpy(&tt[0],&cw[0],8);
+ memcpy(&cw[0],&cw[8],8);
+ memcpy(&cw[8],&tt[0],8);
+ }
+}
+
+// -- cN2ProvLink & cN2Providers -----------------------------------------------
+
+#define N2FLAG_NONE 0
+#define N2FLAG_MECM 1
+#define N2FLAG_Bx 2
+#define N2FLAG_POSTAU 4
+#define N2FLAG_INV 128
+
+class cN2Providers;
+
+class cN2ProvLink {
+friend class cN2Providers;
+private:
+ cN2ProvLink *next;
+protected:
+ int id, flags;
+ //
+ virtual cN2Prov *Create(void)=0;
+ bool CanHandle(int Id) { return ((Id^id)&~0x107)==0; }
+ bool HasFlags(int Flags) { return (flags&Flags)==Flags; }
+public:
+ cN2ProvLink(int Id, int Flags);
+ virtual ~cN2ProvLink() {}
+ };
+
+class cN2Providers {
+friend class cN2ProvLink;
+private:
+ static cN2ProvLink *first;
+ //
+ static void Register(cN2ProvLink *plink);
+public:
+ static cN2Prov *GetProv(int Id, int Flags);
+ };
+
+template<class PROV, int ID, int FLAGS> class cN2ProvLinkReg : public cN2ProvLink {
+public:
+ cN2ProvLinkReg(void):cN2ProvLink(ID,FLAGS) {}
+ virtual cN2Prov *Create(void) { return new PROV(id,flags); }
+ };
+
+cN2ProvLink *cN2Providers::first=0;
+
+void cN2Providers::Register(cN2ProvLink *plink)
+{
+ PRINTF(L_CORE_DYN,"n2providers: registering prov %04X with flags %d",plink->id,plink->flags);
+ plink->next=first;
+ first=plink;
+}
+
+cN2Prov *cN2Providers::GetProv(int Id, int Flags)
+{
+ cN2ProvLink *pl=first;
+ while(pl) {
+ if(pl->CanHandle(Id) && pl->HasFlags(Flags)) return pl->Create();
+ pl=pl->next;
+ }
+ return 0;
+}
+
+cN2ProvLink::cN2ProvLink(int Id, int Flags)
+{
+ id=Id; flags=Flags;
+ cN2Providers::Register(this);
+}
+
+#include "nagra2-prov.c"
+
+#ifndef TESTER
+
+// -- cNagra2 ------------------------------------------------------------------
+
+class cNagra2 : public cNagra {
+private:
+ bool Signature(const unsigned char *vkey, const unsigned char *sig, const unsigned char *msg, int len);
+protected:
+ cIDEA idea;
+ //
+ virtual void CreatePQ(const unsigned char *key, BIGNUM *p, BIGNUM *q);
+ bool DecryptECM(const unsigned char *in, unsigned char *out, const unsigned char *key, int len, const unsigned char *vkey, BIGNUM *m);
+ bool DecryptEMM(const unsigned char *in, unsigned char *out, const unsigned char *key, int len, const unsigned char *vkey, BIGNUM *m);
+ };
+
+void cNagra2::CreatePQ(const unsigned char *key, BIGNUM *p, BIGNUM *q)
+{
+ // Calculate P and Q from PK
+ IdeaKS ks;
+ idea.SetEncKey(key,&ks);
+ // expand IDEA-G key
+ unsigned char idata[96];
+ for(int i=11; i>=0; i--) {
+ unsigned char *d=&idata[i*8];
+ memcpy(d,&key[13],8);
+ *d^=i;
+ idea.Decrypt(d,8,&ks,0);
+ xxor(d,8,d,&key[13]);
+ *d^=i;
+ }
+ // Calculate P
+ idata[0] |= 0x80;
+ idata[47] |= 1;
+ BN_bin2bn(idata,48,p);
+ BN_add_word(p,(key[21] << 5 ) | ((key[22] & 0xf0) >> 3));
+ // Calculate Q
+ idata[48] |= 0x80;
+ idata[95] |= 1;
+ BN_bin2bn(idata+48,48,q);
+ BN_add_word(q,(key[22] &0xf << 9 ) | (key[23]<<1));
+}
+
+bool cNagra2::Signature(const unsigned char *vkey, const unsigned char *sig, const unsigned char *msg, int len)
+{
+ unsigned char buff[16];
+ memcpy(buff,vkey,sizeof(buff));
+ for(int i=0; i<len; i+=8) {
+ IdeaKS ks;
+ idea.SetEncKey(buff,&ks);
+ memcpy(buff,buff+8,8);
+ idea.Encrypt(msg+i,8,buff+8,&ks,0);
+ xxor(&buff[8],8,&buff[8],msg+i);
+ }
+ buff[8]&=0x7F;
+ return (memcmp(sig,buff+8,8)==0);
+}
+
+bool cNagra2::DecryptECM(const unsigned char *in, unsigned char *out, const unsigned char *key, int len, const unsigned char *vkey, BIGNUM *m)
+{
+ int sign=in[0] & 0x80;
+ if(rsa.RSA(out,in+1,64,pubExp,m)<=0) {
+ PRINTF(L_SYS_CRYPTO,"first RSA failed (ECM)");
+ return false;
+ }
+ out[63]|=sign; // sign adjustment
+ if(len>64) memcpy(out+64,in+65,len-64);
+
+ if(in[0]&0x04) {
+ unsigned char tmp[8];
+ DES_key_schedule ks1, ks2;
+ RotateBytes(tmp,&key[0],8);
+ DES_key_sched((DES_cblock *)tmp,&ks1);
+ RotateBytes(tmp,&key[8],8);
+ DES_key_sched((DES_cblock *)tmp,&ks2);
+ memset(tmp,0,sizeof(tmp));
+ for(int i=7; i>=0; i--) RotateBytes(out+8*i,8);
+ DES_ede2_cbc_encrypt(out,out,len,&ks1,&ks2,(DES_cblock *)tmp,DES_DECRYPT);
+ for(int i=7; i>=0; i--) RotateBytes(out+8*i,8);
+ }
+ else idea.Decrypt(out,len,key,0);
+
+ RotateBytes(out,64);
+ if(rsa.RSA(out,out,64,pubExp,m,false)<=0) {
+ PRINTF(L_SYS_CRYPTO,"second RSA failed (ECM)");
+ return false;
+ }
+ if(vkey && !Signature(vkey,out,out+8,len-8)) {
+ PRINTF(L_SYS_CRYPTO,"signature failed (ECM)");
+ return false;
+ }
+ return true;
+}
+
+bool cNagra2::DecryptEMM(const unsigned char *in, unsigned char *out, const unsigned char *key, int len, const unsigned char *vkey, BIGNUM *m)
+{
+ int sign=in[0]&0x80;
+ if(rsa.RSA(out,in+1,96,pubExp,m)<=0) {
+ PRINTF(L_SYS_CRYPTO,"first RSA failed (EMM)");
+ return false;
+ }
+ out[95]|=sign; // sign adjustment
+ cBN exp;
+ if(in[0]&0x08) {
+ // standard IDEA decrypt
+ if(len>96) memcpy(out+96,in+97,len-96);
+ idea.Decrypt(out,len,key,0);
+ BN_set_word(exp,3);
+ }
+ else {
+ // private RSA key expansion
+ CreateRSAPair(key,0,exp,m);
+ }
+ RotateBytes(out,96);
+ if(rsa.RSA(out,out,96,exp,m,false)<=0) {
+ PRINTF(L_SYS_CRYPTO,"second RSA failed (EMM)");
+ return false;
+ }
+ if(vkey && !Signature(vkey,out,out+8,len-8)) {
+ PRINTF(L_SYS_CRYPTO,"signature failed (EMM)");
+ return false;
+ }
+ return true;
+}
+
+// -- cSystemNagra2 ------------------------------------------------------------
+
+class cSystemNagra2 : public cSystem, protected cNagra2 {
+private:
+ int lastEcmId, lastEmmId;
+ cN2Prov *ecmP, *emmP;
+public:
+ cSystemNagra2(void);
+ ~cSystemNagra2();
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+ virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+ };
+
+cSystemNagra2::cSystemNagra2(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+ hasLogger=true;
+ lastEcmId=lastEmmId=0; ecmP=emmP=0;
+}
+
+cSystemNagra2::~cSystemNagra2()
+{
+ delete ecmP;
+ delete emmP;
+}
+
+bool cSystemNagra2::ProcessECM(const cEcmInfo *ecm, unsigned char *data)
+{
+ int cmdLen=data[4]-5;
+ int id=(data[5]*256)+data[6];
+ cTimeMs minTime;
+
+ if(id==0x4101) StartLog(ecm,0x1881); // D+ AU
+
+ if(cmdLen<64 || SCT_LEN(data)<cmdLen+10) {
+ if(doLog) PRINTF(L_SYS_ECM,"bad ECM message msgLen=%d sctLen=%d",cmdLen,SCT_LEN(data));
+ return false;
+ }
+
+ int keyNr=(data[7]&0x10)>>4;
+ cKeySnoop ks(this,'N',id,keyNr);
+ cPlainKey *pk;
+ cBN m1;
+ unsigned char ideaKey[16], vKey[16];
+ bool hasVerifyKey=false;
+ if(!(pk=keys.FindKey('N',id,MBC('M','1'),-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04x M1 key",id);
+ return false;
+ }
+ pk->Get(m1);
+ if((pk=keys.FindKey('N',id,'V',sizeof(vKey)))) {
+ pk->Get(vKey);
+ hasVerifyKey=true;
+ }
+ else if(doLog && id!=lastEcmId) PRINTF(L_SYS_KEY,"missing %04x V key (non-fatal)",id);
+ if(!(pk=keys.FindKey('N',id,keyNr,sizeof(ideaKey)))) return false;
+ pk->Get(ideaKey);
+
+ unsigned char buff[256];
+ if(!DecryptECM(data+9,buff,ideaKey,cmdLen,hasVerifyKey?vKey:0,m1)) {
+ if(doLog) PRINTF(L_SYS_ECM,"decrypt of ECM failed (%04x)",id);
+ return false;
+ }
+
+ if((!ecmP && id!=lastEcmId) || (ecmP && !ecmP->CanHandle(id))) {
+ delete ecmP;
+ ecmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+ if(ecmP) PRINTF(L_SYS_ECM,"provider %04x capabilities%s%s%s%s",id,
+ ecmP->HasFlags(N2FLAG_MECM) ?" MECM":"",
+ ecmP->HasFlags(N2FLAG_Bx) ?" Bx":"",
+ ecmP->HasFlags(N2FLAG_POSTAU) ?" POSTPROCAU":"",
+ ecmP->HasFlags(N2FLAG_INV) ?" INVCW":"");
+ }
+ lastEcmId=id;
+
+ int l=0, mecmAlgo=0;
+ LBSTARTF(L_SYS_ECM);
+ bool contFail=false;
+ for(int i=16; i<cmdLen-10 && l!=3; ) {
+ switch(buff[i]) {
+ case 0x10:
+ case 0x11:
+ if(buff[i+1]==0x09) {
+ int s=(~buff[i])&1;
+ mecmAlgo=buff[i+2]&0x60;
+ memcpy(cw+(s<<3),&buff[i+3],8);
+ i+=11; l|=(s+1);
+ }
+ else {
+ PRINTF(L_SYS_ECM,"bad length %d in CW nano %02x",buff[i+1],buff[i]);
+ i++;
+ }
+ break;
+ case 0x00:
+ i+=2; break;
+ case 0x13 ... 0x17:
+ i+=4; break;
+ case 0x30 ... 0x36:
+ case 0xB0:
+ i+=buff[i+1]+2;
+ break;
+ default:
+ if(!contFail) LBPUT("unknown ECM nano");
+ LBPUT(" %02x",buff[i]);
+ contFail=true;
+ i++;
+ continue;
+ }
+ LBFLUSH(); contFail=false;
+ }
+ LBEND();
+ if(l!=3) return false;
+ if(mecmAlgo>0) {
+ if(ecmP && ecmP->HasFlags(N2FLAG_MECM)) {
+ if(!ecmP->MECM(buff[15],mecmAlgo,cw)) return false;
+ }
+ else { PRINTF(L_SYS_ECM,"MECM for provider %04x not supported",id); return false; }
+ }
+ if(ecmP) ecmP->SwapCW(cw);
+ ks.OK(pk);
+
+ int i=minEcmTime-minTime.Elapsed();
+ if(i>0) cCondWait::SleepMs(i);
+ return true;
+}
+
+void cSystemNagra2::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+ int cmdLen=buffer[9]-5;
+ int id=buffer[10]*256+buffer[11];
+
+ if(cmdLen<96 || SCT_LEN(buffer)<cmdLen+15) {
+ PRINTF(L_SYS_EMM,"bad EMM message msgLen=%d sctLen=%d",cmdLen,SCT_LEN(buffer));
+ return;
+ }
+
+ int keyset=(buffer[12]&0x03);
+ int sel=(buffer[12]&0x10)<<2;
+ int rsasel=(id==0x4101 || id==0x4001) ? 0:sel; // D+ hack
+ int sigsel=(buffer[13]&0x80)>>1;
+ cPlainKey *pk;
+ cBN n;
+ unsigned char ideaKey[24], vKey[16];
+ bool hasVerifyKey=false;
+ if(!(pk=keys.FindKey('N',id,MBC(N2_MAGIC,keyset+0x10+rsasel),96))) {
+ PRINTF(L_SYS_EMM,"missing %04x NN %.02X RSA key (96 bytes)",id,keyset+0x10+rsasel);
+ return;
+ }
+ pk->Get(n);
+ if((pk=keys.FindKey('N',id,MBC(N2_MAGIC,0x03+sigsel),sizeof(vKey)))) {
+ pk->Get(vKey);
+ hasVerifyKey=true;
+ }
+ else if(id!=lastEmmId) PRINTF(L_SYS_EMM,"missing %04x NN %.02X signature key (non-fatal)",id,0x03+sigsel);
+ if(!(pk=keys.FindKey('N',id,MBC(N2_MAGIC,keyset),24))) {
+ if(!(pk=keys.FindKey('N',id,MBC(N2_MAGIC,keyset+sel),16))) {
+ PRINTF(L_SYS_EMM,"missing %04x NN %.02x IDEA key (24 or 16 bytes)",id,keyset+sel);
+ return;
+ }
+ memset(ideaKey+16,0,8);
+ }
+ pk->Get(ideaKey);
+
+ unsigned char emmdata[256];
+ if(!DecryptEMM(buffer+14,emmdata,ideaKey,cmdLen,hasVerifyKey?vKey:0,n)) {
+ PRINTF(L_SYS_EMM,"decrypt of EMM failed (%04x)",id);
+ return;
+ }
+ if((!emmP && id!=lastEmmId) || (emmP && !emmP->CanHandle(id))) {
+ delete emmP;
+ emmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+ if(emmP) PRINTF(L_SYS_EMM,"provider %04x capabilities%s%s%s%s",id,
+ emmP->HasFlags(N2FLAG_MECM) ?" MECM":"",
+ emmP->HasFlags(N2FLAG_Bx) ?" Bx":"",
+ emmP->HasFlags(N2FLAG_POSTAU) ?" POSTPROCAU":"",
+ emmP->HasFlags(N2FLAG_INV) ?" INVCW":"");
+ }
+ lastEmmId=id;
+
+ HEXDUMP(L_SYS_RAWEMM,emmdata,cmdLen,"Nagra2 RAWEMM");
+ id=(emmdata[8]<<8)+emmdata[9];
+ LBSTARTF(L_SYS_EMM);
+ bool contFail=false;
+ for(int i=8+2+4+4; i<cmdLen-22; ) {
+ switch(emmdata[i]) {
+ case 0x42: // plain Key update
+ if(emmdata[i+2]==0x10 && (emmdata[i+3]&0xBF)==0x06 &&
+ (emmdata[i+4]&0xF8)==0x08 && emmdata[i+5]==0x00 && emmdata[i+6]==0x10) {
+ if(!emmP || emmP->PostProcAU(id,&emmdata[i])) {
+ FoundKey();
+ if(keys.NewKey('N',id,(emmdata[i+3]&0x40)>>6,&emmdata[i+7],16)) NewKey();
+ cLoaders::SaveCache();
+ }
+ }
+ i+=23;
+ break;
+ case 0xE0: // DN key update
+ if(emmdata[i+1]==0x25) {
+ FoundKey();
+ if(keys.NewKey('N',id,(emmdata[i+16]&0x40)>>6,&emmdata[i+23],16)) NewKey();
+ cLoaders::SaveCache();
+ }
+ i+=39;
+ break;
+ case 0x83: // change data prov. id
+ id=(emmdata[i+1]<<8)|emmdata[i+2];
+ i+=3;
+ break;
+ case 0xA4: // conditional (always no match assumed for now)
+ i+=emmdata[i+1]+2+4;
+ break;
+ case 0xA6:
+ i+=15;
+ break;
+ case 0xAE:
+ i+=11;
+ break;
+ case 0x13 ... 0x17: // Date
+ i+=4;
+ break;
+ case 0xB0 ... 0xBF: // Update with ROM CPU code
+ {
+ int bx=emmdata[i]&15;
+ if(!emmP || !emmP->HasFlags(N2FLAG_Bx)) {
+ PRINTF(L_SYS_EMM,"B%X for provider %04x not supported",bx,id);
+ i=cmdLen;
+ break;
+ }
+ int r;
+ if((r=emmP->ProcessBx(emmdata,cmdLen,i+1))>0)
+ i+=r;
+ else {
+ PRINTF(L_SYS_EMM,"B%X executing failed for %04x",bx,id);
+ i=cmdLen;
+ }
+ break;
+ }
+ case 0xE3: // Eeprom update
+ i+=emmdata[i+4]+4;
+ break;
+ case 0xE1:
+ case 0xE2:
+ case 0x00: // end of processing
+ i=cmdLen;
+ break;
+ default:
+ if(!contFail) LBPUT("unknown EMM nano");
+ LBPUT(" %02x",emmdata[i]);
+ contFail=true;
+ i++;
+ continue;
+ }
+ LBFLUSH(); contFail=false;
+ }
+ LBEND();
+}
+
+// -- cSystemLinkNagra2 --------------------------------------------------------
+
+static const tI18nPhrase Phrases2[] = {
+ { "Nagra2: AUXserver hostname",
+ "Nagra2: AUXserver Hostname",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Nagra2: AUX-palvelimen osoite",
+ "",
+ "",
+ "",
+ "",
+ },
+ { "Nagra2: AUXserver port",
+ "Nagra2: AUXserver Port",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Nagra2: AUX-palvelimen portti",
+ "",
+ "",
+ "",
+ "",
+ },
+ { "Nagra2: AUXserver password",
+ "Nagra2: AUXserver Passwort",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "Nagra2: AUX-palvelimen salasana",
+ "",
+ "",
+ "",
+ "",
+ },
+ { NULL }
+ };
+
+class cSystemLinkNagra2 : public cSystemLink {
+public:
+ cSystemLinkNagra2(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemNagra2; }
+ };
+
+static cSystemLinkNagra2 staticInitN2;
+
+cSystemLinkNagra2::cSystemLinkNagra2(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+#ifdef HAS_AUXSRV
+ static const char allowed_chars[] = "0123456789abcdefghijklmnopqrstuvwxyz-.";
+ opts=new cOpts(SYSTEM_NAME,3);
+ opts->Add(new cOptStr("AuxServerAddr","Nagra2: AUXserver hostname",auxAddr,sizeof(auxAddr),allowed_chars));
+ opts->Add(new cOptInt("AuxServerPort","Nagra2: AUXserver port",&auxPort,0,65535));
+ opts->Add(new cOptStr("AuxServerPass","Nagra2: AUXserver password",auxPassword,sizeof(auxPassword),allowed_chars));
+ Feature.AddPhrases(Phrases2);
+#endif
+ Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkNagra2::CanHandle(unsigned short SysId)
+{
+ return ((SysId&SYSTEM_MASK)==SYSTEM_NAGRA && (SysId&0xFF)>0) ||
+ SysId==SYSTEM_NAGRA_BEV;
+}
+
+#endif //TESTER
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "parse.h"
+#include "misc.h"
+#include "log-sc.h"
+
+#define SYSTEM_CONAX 0x0B00
+
+#define SYSTEM_NAME "SC-Conax"
+#define SYSTEM_PRI -5
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_CONAX)
+
+#define SC_NAME "Conax"
+#define SC_ID MAKE_SC_ID('C','o','n','x')
+
+#define L_SC 9
+#define L_SC_ALL LALL(L_SC_LASTDEF)
+
+static const struct LogModule lm_sc = {
+ (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+ "sc-conax",
+ { L_SC_DEFNAMES }
+ };
+ADD_MODULE(L_SC,lm_sc)
+
+// -- cSystemScConax ------------------------------------------------------------------
+
+class cSystemScConax : public cSystemScCore {
+public:
+ cSystemScConax(void);
+ };
+
+cSystemScConax::cSystemScConax(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Conax")
+{
+ hasLogger=true;
+}
+
+// -- cSystemLinkScConax --------------------------------------------------------------
+
+class cSystemLinkScConax : public cSystemLink {
+public:
+ cSystemLinkScConax(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemScConax; }
+ };
+
+static cSystemLinkScConax staticInit;
+
+cSystemLinkScConax::cSystemLinkScConax(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScConax::CanHandle(unsigned short SysId)
+{
+ bool res=false;
+ cSmartCard *card=smartcards.LockCard(SC_ID);
+ if(card) {
+ res=card->CanHandle(SysId);
+ smartcards.ReleaseCard(card);
+ }
+ return res;
+}
+
+// -- cSmartCardConax -----------------------------------------------------------------
+
+#define STDENTTAG 0x32
+#define PPVENTTAG 0x39
+#define ADDR_SIZE 7
+
+struct stdent {
+ unsigned short id;
+ char name[16], date[4][16];
+ unsigned int pbm[2];
+ };
+
+class cSmartCardConax : public cSmartCard, private cProviders {
+private:
+ int sysId, cardVer, currency;
+ bool emmInitDone;
+ //
+ int GetLen(void);
+public:
+ cSmartCardConax(void);
+ virtual bool Init(void);
+ virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+ virtual bool Update(int pid, int caid, const unsigned char *data);
+ virtual bool CanHandle(unsigned short SysId);
+ };
+
+static const struct StatusMsg msgs[] = {
+ { { 0x90,0x00 }, "Instruction executed without errors", true },
+ { { 0x90,0x11 }, "Bad instruction", false },
+ { { 0x90,0x12 }, "Access denied, check your subscription", false },
+ { { 0x90,0x13 }, "Bit error detected", false },
+ { { 0x90,0x16 }, "Insuficient info", false },
+ { { 0x94,0x00 }, "Instruction executed without errors, waiting for user interaction", true },
+ { { 0x98,0xFF }, "Instruction accepted, data to be read", true },
+ { { 0x9c,0xFF }, "Instruction accepted, data to be read", true },
+ { { 0xFF,0xFF }, 0, false }
+ };
+
+static const struct CardConfig cardCfg = {
+ SM_8E2,1000,100
+ };
+
+cSmartCardConax::cSmartCardConax(void)
+:cSmartCard(&cardCfg,msgs)
+{
+ sysId=0; cardVer=0; currency=0;
+ emmInitDone=false;
+}
+
+bool cSmartCardConax::CanHandle(unsigned short SysId)
+{
+ return (SysId==sysId);
+}
+
+int cSmartCardConax::GetLen(void)
+{
+ return (sb[0]==0x98 || sb[0]==0x9C || (sb[0]==0x90 && sb[1]==0x00)) ? sb[1] : -1;
+}
+
+bool cSmartCardConax::Init(void)
+{
+ static const unsigned char cnxHist[] = { '0','B','0','0' }; // XXX is this always true ?
+
+ sysId=0; cardVer=0; currency=0;
+ if(atr->histLen<4 || memcmp(atr->hist, cnxHist, atr->histLen)) {
+ PRINTF(L_SC_INIT,"doesn't look like a Conax card");
+ return false;
+ }
+
+ infoStr.Begin();
+ infoStr.Strcat("Conax smartcard\n");
+ static unsigned char ins26[] = { 0xdd, 0x26, 0x00, 0x00, 0x03 };
+ static unsigned char insc6[] = { 0xdd, 0xc6, 0x00, 0x00, 0x03 };
+ static unsigned char insca[] = { 0xdd, 0xca, 0x00, 0x00, 0x00 };
+ static const unsigned char hostVer[] = { 0x10,0x01,0x01 }; // host version
+ unsigned char buff[MAX_LEN];
+ int l;
+ if(!IsoWrite(ins26,hostVer) || !Status() || (l=GetLen())<=0) {
+ PRINTF(L_SC_ERROR,"card init failed (1)");
+ return false;
+ }
+ insca[4]=l;
+ if(!IsoRead(insca,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"card init failed (2)");
+ return false;
+ }
+ for(int i=0; i<l; i+=buff[i+1]+2) {
+ switch(buff[i]) {
+ case 0x20: cardVer=buff[i+2]; break;
+ case 0x28: sysId=(buff[i+2]<<8)+buff[i+3]; break;
+ case 0x2f: currency=(buff[i+2]<<8)+buff[i+3]; break;
+ }
+ }
+ infoStr.Printf("Card v.%d Caid %04x\n",cardVer,sysId);
+ PRINTF(L_SC_INIT,"card v.%d",cardVer);
+ snprintf(idStr,sizeof(idStr),"%s (V.%d)",SC_NAME,cardVer);
+
+ static const unsigned char stdEntits[] = { 0x1C,0x01,0x00 };
+ if(IsoWrite(insc6,stdEntits) && Status() && (l=GetLen())>0) {
+ infoStr.Printf("Subscriptions:\n");
+ infoStr.Printf("|id |name |date |\n");
+ infoStr.Printf("+----+------------+-----------------+\n");
+
+ do {
+ insca[4]=l;
+ if(!IsoRead(insca,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to read entitlements");
+ break;
+ }
+ for(int i=0; i<l; i+=buff[i+1]+2) {
+ if(buff[i]!=STDENTTAG) {
+ PRINTF(L_SC_ERROR,"bad entitlement format %02x != %02x",buff[i],STDENTTAG);
+ break;
+ }
+
+ struct stdent ent;
+ memset(&ent,0,sizeof(ent));
+ ent.id=(buff[i+2]<<8)|buff[i+3];
+
+ int date=0, pbm=0;
+ int max=i+buff[i+1]+2;
+ for(int j=i+4; j<max; j+=buff[j+1]+2) {
+ switch(buff[j]) {
+ case 0x01: // prov. name
+ snprintf(ent.name,sizeof(ent.name),"%.12s",&buff[j+2]);
+ break;
+ case 0x30: // date
+ if(date<=3) {
+ snprintf(ent.date[date],sizeof(ent.date[0]),"%02d.%02d.%02d",buff[j+2]&0x1F,buff[j+3]&0xF,(1990+((buff[j+3]>>4)+((buff[j+2]>>5)&0x7)*10))%100);
+ date++;
+ }
+ break;
+ case 0x20:
+ if(pbm<=1) {
+ ent.pbm[pbm]=(buff[j+2]<<24)|(buff[j+3]<<16)|(buff[j+4]<<8)|buff[j+5];
+ pbm++;
+ }
+ break;
+ }
+ }
+ infoStr.Printf("|%04X|%s|%s-%s|\n",ent.id,ent.name,ent.date[0],ent.date[1]);
+ infoStr.Printf("| | |%s-%s\n",ent.date[2],ent.date[3]);
+ }
+ } while((l=GetLen())>0);
+ }
+ else
+ PRINTF(L_SC_ERROR,"requesting entitlements failed");
+
+/*
+ static const unsigned char ppvEntits[] = { 0x1C,0x01,0x01 };
+ if(IsoWrite(ins26,ppvEntits) && Status()) {
+ while((l=GetLen())>0) {
+ insca[4]=l;
+ if(!IsoRead(insca,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to read PPV entitlements");
+ break;
+ }
+
+
+ }
+ }
+ else
+ PRINTF(L_SC_ERROR,"getting PPV entitlements failed");
+*/
+
+ emmInitDone=false;
+ infoStr.Finish();
+ return true;
+}
+
+bool cSmartCardConax::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+ static unsigned char insa2[] = { 0xDD,0xA2,0x00,0x00,0x00 };
+ static unsigned char insca[] = { 0xDD,0xCA,0x00,0x00,0x00 };
+
+ int l;
+ if((l=CheckSctLen(data,3))<=0) return false;
+ unsigned char buff[MAX_LEN];
+ buff[0]=0x14;
+ buff[1]=l+1;
+ buff[2]=0;
+ memcpy(buff+3,data,l);
+
+ insa2[4]=l+3;
+ if(!IsoWrite(insa2,buff) || !Status() || (l=GetLen())<=0) return false;
+ int gotIdx=0;
+ do {
+ insca[4]=l;
+ if(!IsoRead(insca,buff) || !Status()) return false;
+ for(int i=0; i<l; i+=buff[i+1]+2) {
+ switch(buff[i]) {
+ case 0x25:
+ if(buff[i+1]>=13) {
+ int idx=buff[i+4];
+ if(idx<=1) {
+ gotIdx|=(1<<idx);
+ memcpy(cw+idx*8,&buff[i+7],8);
+ }
+ }
+ break;
+ }
+ }
+ } while((l=GetLen())>0);
+ if(gotIdx!=3) PRINTF(L_SC_ERROR,"strange, only got index %d cw. Failing... (this may be a bug)",gotIdx==1?0:1);
+ return gotIdx==3;
+}
+
+bool cSmartCardConax::Update(int pid, int caid, const unsigned char *data)
+{
+ static unsigned char ins82[] = { 0xdd,0x82,0x00,0x00,0x10 };
+ static unsigned char ins84[] = { 0xdd,0x84,0x00,0x00,0x00 };
+ static unsigned char insca[] = { 0xdd,0xca,0x00,0x00,0x00 };
+ static unsigned char ins82Data[] = { 0x11,0x0e,0x01,0xb0,0x0f,0xff,0xff,0xdd,0x00,0x00,0x09,0x04,0x0b,0x00,0x00,0x00 };
+ unsigned char buff[MAX_LEN];
+
+ if(!emmInitDone) {
+ ins82Data[14]=(pid>>8)&0xFF;
+ ins82Data[15]=pid&0xFF;
+ int l;
+ if(!IsoWrite(ins82,ins82Data) || !Status() || (l=GetLen())<=0) return false;
+ insca[4]=l;
+ if(!IsoRead(insca,buff) || !Status()) return false;
+ if(buff[0]!=0x22 || buff[1]+2!=l) {
+ PRINTF(L_SC_ERROR,"bad card reply on EMM init");
+ return false;
+ }
+
+ LBSTARTF(L_SC_INIT);
+ LBPUT("set filter");
+ Clear();
+ for(int i=2; i<l; i+=buff[i+1]+2) {
+ if(buff[i]==0x23) { // Card Address
+ if(buff[i+1]==ADDR_SIZE) {
+ AddProv(new cProviderConax(&buff[i+2]));
+ char str[ADDR_SIZE*2+4];
+ LBPUT(" addr %s",HexStr(str,&buff[i+2],ADDR_SIZE));
+ }
+ else {
+ LBPUT(" bad addr cmd (size=%d)",buff[i+1]);
+ return false;
+ }
+ }
+ }
+ LBEND();
+ emmInitDone=true;
+ }
+
+ if(MatchProv(data)) {
+ int l;
+ if((l=CheckSctLen(data,2))>0) {
+ buff[0]=0x12; buff[1]=l;
+ memcpy(&buff[2],data,l);
+ ins84[4]=l+2;
+ if(IsoWrite(ins84,buff) && !Status()) return true;
+ }
+ }
+ return false;
+}
+
+// -- cSmartCardLinkConax -------------------------------------------------------------
+
+class cSmartCardLinkConax : public cSmartCardLink {
+public:
+ cSmartCardLinkConax(void):cSmartCardLink(SC_NAME,SC_ID) {}
+ virtual cSmartCard *Create(void) { return new cSmartCardConax(); }
+ };
+
+static cSmartCardLinkConax staticScInit;
--- /dev/null
+#
+# Smartcard Conax
+#
+TARGET = sc_conax
+OBJS = sc-conax.o
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "data.h"
+#include "parse.h"
+#include "crypto.h"
+#include "opts.h"
+#include "misc.h"
+#include "log-sc.h"
+
+#define SYSTEM_NAME "SC-Cryptoworks"
+#define SYSTEM_PRI -5
+
+#define SC_NAME "Cryptoworks"
+#define SC_ID MAKE_SC_ID('C','r','W','o')
+
+#define L_SC 10
+#define L_SC_EXTRA LCLASS(L_SC,L_SC_LASTDEF<<1)
+#define L_SC_ALL LALL(L_SC_EXTRA)
+
+static const struct LogModule lm_sc = {
+ (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+ "sc-cryptoworks",
+ { L_SC_DEFNAMES,"extra" }
+ };
+ADD_MODULE(L_SC,lm_sc)
+
+static int disableParental=0;
+
+// -- cSystemScCryptoworks ---------------------------------------------------------------
+
+class cSystemScCryptoworks : public cSystemScCore {
+public:
+ cSystemScCryptoworks(void);
+ };
+
+cSystemScCryptoworks::cSystemScCryptoworks(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Cryptoworks")
+{
+ hasLogger=true;
+}
+
+// -- cSystemLinkScCryptoworks --------------------------------------------------------
+
+static const tI18nPhrase Phrases[] = {
+ { "SC-Cryptoworks: Parental rating",
+ "SC-Cryptoworks: Altersbeschränkung",
+ "",
+ "",
+ "SC-Cryptoworks: Leeftijdsadvies",
+ "",
+ "SC-Cryptoworks: Autorisation parentale",
+ "",
+ "SC-Cryptoworks: Ikäraja",
+ "SC-Cryptoworks: wska¼nik rodzica",
+ "",
+ "",
+ "",
+ },
+ { "don't touch",
+ "nicht ändern",
+ "",
+ "",
+ "niet wijzigen",
+ "",
+ "Ne pas modifier",
+ "",
+ "älä koske",
+ "nie dotykaj",
+ "",
+ "",
+ "",
+ },
+ { "disable",
+ "ausschalten",
+ "",
+ "",
+ "uitschakelen",
+ "",
+ "Désactiver",
+ "",
+ "poista",
+ "wy³±cz",
+ "",
+ "",
+ "",
+ },
+ { NULL }
+ };
+
+class cSystemLinkScCryptoworks : public cSystemLink {
+public:
+ cSystemLinkScCryptoworks(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemScCryptoworks; }
+ };
+
+static cSystemLinkScCryptoworks staticInit;
+
+cSystemLinkScCryptoworks::cSystemLinkScCryptoworks(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ static const char *rat[] = {
+ "don't touch",
+ "disable"
+ };
+
+ opts=new cOpts(SYSTEM_NAME,1);
+ opts->Add(new cOptSel("DisableParental","SC-Cryptoworks: Parental rating",&disableParental,sizeof(rat)/sizeof(char *),rat));
+ Feature.NeedsSmartCard();
+ Feature.AddPhrases(Phrases);
+}
+
+bool cSystemLinkScCryptoworks::CanHandle(unsigned short SysId)
+{
+ bool res=false;
+ cSmartCard *card=smartcards.LockCard(SC_ID);
+ if(card) {
+ res=card->CanHandle(SysId);
+ smartcards.ReleaseCard(card);
+ }
+ return res;
+}
+
+// -- cSmartCardDataCryptoworks -----------------------------------------------------
+
+enum eDataType { dtIPK, dtUCPK, dtPIN };
+
+class cSmartCardDataCryptoworks : public cSmartCardData {
+private:
+ int IdLen(void) const { return type==dtIPK ? 2:5; }
+public:
+ eDataType type;
+ unsigned char id[5], pin[4];
+ cBN key;
+ //
+ cSmartCardDataCryptoworks(void);
+ cSmartCardDataCryptoworks(eDataType Type, unsigned char *Id);
+ virtual bool Parse(const char *line);
+ virtual bool Matches(cSmartCardData *param);
+ };
+
+cSmartCardDataCryptoworks::cSmartCardDataCryptoworks(void)
+:cSmartCardData(SC_ID)
+{}
+
+cSmartCardDataCryptoworks::cSmartCardDataCryptoworks(eDataType Type, unsigned char *Id)
+:cSmartCardData(SC_ID)
+{
+ type=Type;
+ memset(id,0,sizeof(id));
+ memcpy(id,Id,IdLen());
+}
+
+bool cSmartCardDataCryptoworks::Matches(cSmartCardData *param)
+{
+ cSmartCardDataCryptoworks *cd=(cSmartCardDataCryptoworks *)param;
+ return cd->type==type && !memcmp(cd->id,id,IdLen());
+}
+
+bool cSmartCardDataCryptoworks::Parse(const char *line)
+{
+ line=skipspace(line);
+ if(!strncasecmp(line,"IPK",3)) { type=dtIPK; line+=3; }
+ else if(!strncasecmp(line,"UCPK",4)) { type=dtUCPK; line+=4; }
+ else if(!strncasecmp(line,"PIN",3)) { type=dtPIN; line+=3; }
+ else {
+ PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: datatype");
+ return false;
+ }
+ line=skipspace(line);
+ if(GetHex(line,id,IdLen())!=IdLen()) {
+ PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: caid/serial");
+ return false;
+ }
+ line=skipspace(line);
+ if(type==dtPIN) {
+ for(int i=0; i<4; i++) {
+ if(!isdigit(*line)) {
+ PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: pin");
+ return false;
+ }
+ pin[i]=*line++;
+ }
+ }
+ else {
+ unsigned char buff[64];
+ if(GetHex(line,buff,64,true)!=64) {
+ PRINTF(L_CORE_LOAD,"smartcarddatacryptoworks: format error: ipk/ucpk");
+ return false;
+ }
+ BN_bin2bn(buff,64,key);
+ }
+ return true;
+}
+
+// -- cSmartCardCryptoworks -----------------------------------------------------------
+
+class cSmartCardCryptoworks : public cSmartCard, public cIdSet {
+private:
+ int caid;
+ cRSA rsa;
+ cBN ucpk, exp;
+ bool ucpkValid;
+ //
+ int GetLen(void);
+ bool EndOfData(void);
+ bool SelectFile(int file);
+ int ReadRecord(unsigned char *buff, int num);
+ int ReadData(unsigned char *buff, int len);
+public:
+ cSmartCardCryptoworks(void);
+ virtual bool Init(void);
+ virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+ virtual bool Update(int pid, int caid, const unsigned char *data);
+ virtual bool CanHandle(unsigned short CaId);
+ };
+
+static const struct StatusMsg msgs[] = {
+ { { 0x90,0x00 }, "Instruction executed without errors", true },
+ { { 0x92,0x40 }, "Memory problem", false },
+ { { 0x94,0x02 }, "Out of range", false },
+ { { 0x94,0x04 }, "File not found", false },
+ { { 0x98,0x04 }, "Verification failed (wrong PIN)", false },
+ { { 0x98,0x05 }, "Wrong signature", false },
+ { { 0x98,0x40 }, "Verification failed, card blocked (deblocking by uplink required)", false },
+ { { 0x9F,0xFF }, "Instruction accepted, data to be read", true },
+ { { 0xFF,0xFF }, 0, false }
+ };
+
+static const struct CardConfig cardCfg = {
+ SM_8E2,1000,100
+ };
+
+struct chid_dat {
+ unsigned int chid, version;
+ unsigned char id, status;
+ char from[16], to[16], name[16];
+ };
+
+cSmartCardCryptoworks::cSmartCardCryptoworks(void)
+:cSmartCard(&cardCfg,msgs)
+{
+ static const unsigned char cwexp[] = { 0x01,0x00,0x01 };
+ BN_bin2bn(cwexp,sizeof(cwexp),exp);
+ ucpkValid=false;
+}
+
+int cSmartCardCryptoworks::GetLen(void)
+{
+ return (sb[0]==0x9F) ? sb[1] : -1;
+}
+
+bool cSmartCardCryptoworks::EndOfData(void)
+{
+ return sb[0]==0x94 && sb[1]==0x02;
+}
+
+bool cSmartCardCryptoworks::SelectFile(int file)
+{
+ static unsigned char insa4[] = { 0xA4,0xA4,0x00,0x00,0x02,0x00,0x00 };
+ insa4[5]=file>>8;
+ insa4[6]=file&0xFF;
+ return IsoWrite(insa4,&insa4[5]) && Status();
+}
+
+int cSmartCardCryptoworks::ReadRecord(unsigned char *buff, int num)
+{
+ static unsigned char insa2[] = { 0xA4,0xA2,0x00,0x00,0x01,0x00 };
+ static unsigned char insb2[] = { 0xA4,0xB2,0x00,0x00,0x00 };
+ insa2[5]=num;
+ if(IsoWrite(insa2,&insa2[5]) && Status() && (num=GetLen())>0) {
+ insb2[4]=num;
+ if(IsoRead(insb2,buff) && Status()) return num;
+ }
+ return -1;
+}
+
+int cSmartCardCryptoworks::ReadData(unsigned char *buff, int len)
+{
+ static unsigned char insc0[] = { 0xA4,0xC0,0x00,0x00,0x00 };
+ insc0[4]=len;
+ if(IsoRead(insc0,buff) && Status()) return len;
+ return -1;
+}
+
+bool cSmartCardCryptoworks::Init(void)
+{
+ if(atr->histLen<6 || atr->hist[1]!=0xC4 || atr->hist[4]!=0x8F || atr->hist[5]!=0xF1) {
+ PRINTF(L_SC_INIT,"doesn't look like a Cryptoworks card");
+ return false;
+ }
+ infoStr.Begin();
+ infoStr.Strcat("Cryptoworks smartcard\n");
+ ucpkValid=false;
+ unsigned char buff[MAX_LEN];
+ int mfid=0x3F20;
+ if(ReadData(buff,0x11)>0 && buff[0]==0xDF && buff[1]>=6)
+ mfid=buff[6]*256+buff[7];
+ else PRINTF(L_SC_ERROR,"reading MF-ID failed, using default 3F20");
+
+ unsigned char Caid[2], serial[5];
+ if(!SelectFile(0x2F01) || ReadRecord(buff,0xD1)<4) {
+ PRINTF(L_SC_ERROR,"reading record 2f01/d1 failed");
+ return false;
+ }
+ memcpy(Caid,&buff[2],2);
+ caid=buff[2]*256+buff[3];
+ if(ReadRecord(buff,0x80)<7) {
+ PRINTF(L_SC_ERROR,"reading record 2f01/80 failed");
+ return false;
+ }
+ SetCard(new cCardCryptoworks(&buff[2]));
+ memcpy(serial,&buff[2],5);
+ snprintf(idStr,sizeof(idStr),"%s (V.%d)",SC_NAME,atr->hist[2]);
+ char str[20];
+ infoStr.Printf("Card v.%d (PINcount=%d)\n"
+ "Caid %04x Serial %s\n",
+ atr->hist[2],atr->hist[3],caid,HexStr(str,&buff[2],5));
+ PRINTF(L_SC_INIT,"card v.%d (pindown=%d) caid %04x serial %s MF %04X",atr->hist[2],atr->hist[3],caid,HexStr(str,&buff[2],5),mfid);
+ if(ReadRecord(buff,0x9F)>=3) {
+ char *n="(unknown)";
+ if(ReadRecord(buff+10,0xC0)>=18) n=(char *)buff+10+2;
+ infoStr.Printf("Issuer: 0x%02x (%.16s)\n",buff[2],n);
+ PRINTF(L_SC_INIT,"card issuer: 0x%02x %.16s",buff[2],n);
+ }
+ if(ReadRecord(buff,0x9E)>=66) {
+ HEXDUMP(L_SC_EXTRA,&buff[2],64,"card ISK");
+ cSmartCardDataCryptoworks cd(dtIPK,Caid);
+ cSmartCardDataCryptoworks *entry=(cSmartCardDataCryptoworks *)smartcards.FindCardData(&cd);
+ if(entry) {
+ PRINTF(L_SC_EXTRA,"got IPK from smartcard.conf");
+ if(rsa.RSA(&buff[2],&buff[2],64,exp,entry->key,false)>0) {
+ HEXDUMP(L_SC_EXTRA,&buff[2],64,"decrypted ISK");
+ if(buff[2] == ((mfid&0xFF)>>1)) {
+ buff[2]|=0x80;
+ BN_bin2bn(&buff[2],64,ucpk);
+ ucpkValid=true;
+ PRINTF(L_SC_INIT,"got UCPK from IPK/ISK");
+ }
+ else PRINTF(L_SC_ERROR,"UCPK check failed %02x != %02x",buff[2],(mfid&0xFF)>>1);
+ }
+ else PRINTF(L_SC_ERROR,"RSA failed for UCPK");
+ }
+ }
+ if(!ucpkValid) {
+ cSmartCardDataCryptoworks cd(dtUCPK,serial);
+ cSmartCardDataCryptoworks *entry=(cSmartCardDataCryptoworks *)smartcards.FindCardData(&cd);
+ if(entry) {
+ BN_copy(ucpk,entry->key);
+ ucpkValid=true;
+ PRINTF(L_SC_INIT,"got UCPK from smartcard.conf");
+ }
+ }
+ if(!ucpkValid) PRINTF(L_GEN_WARN,"no valid UCPK for cryptoworks smartcard");
+
+ // read entitlements
+ static unsigned char insb8[] = { 0xA4,0xB8,0x00,0x00,0x0C };
+ unsigned char provId[16];
+ unsigned int count=0;
+ insb8[2]=insb8[3]=0x00;
+ while(IsoRead(insb8,buff) && !EndOfData() && Status()) {
+ if(buff[0]==0xDF && buff[1]==0x0A) {
+ int fileno=(buff[4]&0x3F)*256+buff[5];
+ if((fileno&0xFF00)==0x1F00) {
+ provId[count++]=fileno&0xFF;
+ if(count>=sizeof(provId)) break;
+ }
+ }
+ insb8[2]=insb8[3]=0xFF;
+ }
+ for(unsigned int i=0; i<count ; i++) {
+ if(SelectFile(0x1F00+provId[i])) {
+ char *n="(unknown)";
+ if(SelectFile(0x0E11) && ReadRecord(buff,0xD6)>=18) n=(char *)buff+2;
+ infoStr.Printf("Provider %d: 0x%02x (%.16s)\n",i,provId[i],n);
+ PRINTF(L_SC_INIT,"provider %d: 0x%02x %.16s",i,provId[i],n);
+ static unsigned char insa2_0[] = { 0xA4,0xA2,0x01,0x00,0x03,0x83,0x01,0x00 };
+ static unsigned char insa2_1[] = { 0xA4,0xA2,0x01,0x00,0x05,0x8C,0x00,0x00,0x00,0x00 };
+ static unsigned char insb2[] = { 0xA4,0xB2,0x00,0x00,0x00 };
+ static const unsigned int fn[] = { 0x0f00,0x0f20,0x0f40,0x0f60,0 };
+ static const char *fnName[] = { "Download","Subscriptions","PPV Events","PPV Events",0 };
+ bool first=true;
+ for(int j=0; fn[j]; j++) {
+ if(SelectFile(fn[j])) {
+ insa2_0[7]=provId[i];
+ unsigned char *ins=(j==1) ? insa2_1 : insa2_0;
+ int l;
+ if(IsoWrite(ins,&ins[5]) && Status() && (l=GetLen())>0) {
+ if(first) {
+ infoStr.Printf("id|chid|st|date |name\n");
+ PRINTF(L_SC_INIT,"| id | chid | stat | date | version | name ");
+ first=false;
+ }
+ infoStr.Printf( "--+----+--+-----------------+------\n"
+ "%s\n",
+ fnName[j]);
+ PRINTF(L_SC_INIT,"+----+------+------+-------------------+----------+-------------");
+ PRINTF(L_SC_INIT,"| %s",fnName[j]);
+ insb2[3]=0x00;
+ insb2[4]=l;
+ while(IsoRead(insb2,buff) && !EndOfData() && Status()) {
+ struct chid_dat chid;
+ memset(&chid,0,sizeof(chid));
+ for(int k=0; k<l; k+=buff[k+1]+2) {
+ switch(buff[k]) {
+ case 0x83:
+ chid.id=buff[k+2];
+ break;
+ case 0x8c:
+ chid.status=buff[k+2];
+ chid.chid=buff[k+3]*256+buff[k+4];
+ break;
+ case 0x8D:
+ snprintf(chid.from,sizeof(chid.from),"%02d.%02d.%02d",buff[k+3]&0x1F,((buff[k+2]&1)<<3)+(buff[k+3]>>5),(1990+(buff[k+2]>>1))%100);
+ snprintf(chid.to ,sizeof(chid.to) ,"%02d.%02d.%02d",buff[k+5]&0x1F,((buff[k+4]&1)<<3)+(buff[k+5]>>5),(1990+(buff[k+4]>>1))%100);
+ break;
+ case 0xD5:
+ snprintf(chid.name,sizeof(chid.name),"%.12s",&buff[k+2]);
+ chid.version=Bin2Int(&buff[k+14],4);
+ break;
+ }
+ }
+ infoStr.Printf("%02x|%04x|%02x|%s-%s|%.12s\n",
+ chid.id,chid.chid,chid.status,chid.from,chid.to,chid.name);
+ PRINTF(L_SC_INIT,"| %02x | %04x | %02x | %s-%s | %08x | %.12s",
+ chid.id,chid.chid,chid.status,chid.from,chid.to,chid.version,chid.name);
+ insb2[3]=0x01;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if(disableParental) {
+ bool pinOK=false;
+ if(SelectFile(0x2F11) && ReadRecord(buff,atr->hist[3])>=7) {
+ pinOK=true;
+ PRINTF(L_SC_INIT,"read PIN from card.");
+ }
+ if(!pinOK) {
+ cSmartCardDataCryptoworks cd(dtPIN,serial);
+ cSmartCardDataCryptoworks *entry=(cSmartCardDataCryptoworks *)smartcards.FindCardData(&cd);
+ if(entry) {
+ memcpy(&buff[2],entry->pin,4);
+ pinOK=true;
+ PRINTF(L_SC_INIT,"got PIN from smartcard.conf.");
+ }
+ }
+ if(pinOK) {
+ static const unsigned char ins24[] = { 0xA4,0x24,0x00,0x01,0x05 };
+ PRINTF(L_SC_INIT,"your card PIN is %.4s",&buff[2]);
+ // parental rating
+ // 0x00 - undefined
+ // 0x01-0x0f - minimum age=rating+3 years
+ // 0x10-0xff - reserved for provider usage
+ buff[6]=0;
+ if(IsoWrite(ins24,&buff[2]) && Status())
+ PRINTF(L_SC_INIT,"parental rating set to %02x",buff[6]);
+ else
+ PRINTF(L_SC_ERROR,"failed to set parental rating.");
+ }
+ else PRINTF(L_SC_INIT,"no PIN available.");
+ }
+
+ infoStr.Finish();
+ return true;
+}
+
+bool cSmartCardCryptoworks::CanHandle(unsigned short CaId)
+{
+ return CaId==caid;
+}
+
+bool cSmartCardCryptoworks::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+ static unsigned char ins4c[] = { 0xA4,0x4C,0x00,0x00,0x00 };
+
+ unsigned char nanoD4[10];
+ int l=CheckSctLen(data,-5+(ucpkValid ? sizeof(nanoD4):0));
+ if(l>5) {
+ unsigned char buff[MAX_LEN];
+ if(ucpkValid) {
+ memcpy(buff,data,l);
+ nanoD4[0]=0xD4;
+ nanoD4[1]=0x08;
+ for(unsigned int i=2; i<sizeof(nanoD4); i++) nanoD4[i]=rand();
+ memcpy(&buff[l],nanoD4,sizeof(nanoD4));
+ data=buff; l+=sizeof(nanoD4);
+ }
+ ins4c[3]=ucpkValid ? 2 : 0;
+ ins4c[4]=l-5;
+ if(IsoWrite(ins4c,&data[5]) && Status() &&
+ (l=GetLen())>0 && ReadData(buff,l)==l) {
+ int r=0;
+ for(int i=0; i<l && r<2; ) {
+ int n=buff[i+1];
+ switch(buff[i]) {
+ case 0xDB: // CW
+ PRINTF(L_SC_EXTRA,"nano DB (cw)");
+ if(n==0x10) {
+ memcpy(cw,&buff[i+2],16);
+ r|=1;
+ }
+ break;
+ case 0xDF: // signature
+ PRINTF(L_SC_EXTRA,"nano DF %02x (sig)",n);
+ if(n==0x08) {
+ if((buff[i+2]&0x50)==0x50) {
+ if(buff[i+3]&0x01) PRINTF(L_SC_ERROR,"not decyphered. PIN protected?");
+ else if(!(buff[i+5]&0x80)) PRINTF(L_SC_ERROR,"missing entitlement. Check your subscription");
+ else r|=2;
+ }
+ }
+ else if(n==0x40) { // camcrypt
+ if(ucpkValid) {
+ if(rsa.RSA(&buff[i+2],&buff[i+2],n,exp,ucpk,false)<=0) {
+ PRINTF(L_SC_ERROR,"camcrypt RSA failed.");
+ return false;
+ }
+ HEXDUMP(L_SC_EXTRA,&buff[i+2],n,"after camcrypt");
+ if(!memmem(&buff[i+2+4],n-4,nanoD4,sizeof(nanoD4))) {
+ PRINTF(L_SC_ERROR,"camcrypt failed. Check IPK/UCPK in your smartcard.conf!");
+ return false;
+ }
+ r=0; l=n-4; n=4;
+ }
+ else {
+ PRINTF(L_SC_ERROR,"valid UCPK needed for camcrypt! Check your smartcard.conf for a IPK/UCPK!");
+ return false;
+ }
+ }
+ break;
+ default:
+ PRINTF(L_SC_EXTRA,"nano %02x (unhandled)",buff[i]);
+ break;
+ }
+ i+=n+2;
+ }
+ return r==3;
+ }
+ }
+ return false;
+}
+
+bool cSmartCardCryptoworks::Update(int pid, int caid, const unsigned char *data)
+{
+ static unsigned char ins[] = { 0xA4,0x42,0x00,0x00,0x00 };
+
+ cAssembleData ad(data);
+ if(MatchAndAssemble(&ad,0,0)) {
+ while((data=ad.Assembled())) {
+ int c, n;
+ switch(data[0]) {
+ case 0x82: c=0x42; n=10; break;
+ case 0x84: c=0x48; n=9; break;
+ case 0x88:
+ case 0x89: c=0x44; n=5; break;
+ default: continue;
+ }
+
+ int len=CheckSctLen(data,-n);
+ if(len>n) {
+ ins[1]=c;
+ ins[4]=len-n;
+ if(IsoWrite(ins,&data[n])) Status();
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+// -- cSmartCardLinkCryptoworks -------------------------------------------------------
+
+class cSmartCardLinkCryptoworks : public cSmartCardLink {
+public:
+ cSmartCardLinkCryptoworks(void):cSmartCardLink(SC_NAME,SC_ID) {}
+ virtual cSmartCard *Create(void) { return new cSmartCardCryptoworks(); }
+ virtual cSmartCardData *CreateData(void) { return new cSmartCardDataCryptoworks; }
+ };
+
+static cSmartCardLinkCryptoworks staticScInit;
--- /dev/null
+#
+# Smartcard Cryptoworks
+#
+TARGET = sc_cryptoworks
+OBJS = sc-cryptoworks.o
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include <openssl/rand.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "crypto.h"
+#include "data.h"
+#include "misc.h"
+#include "parse.h"
+#include "log-sc.h"
+#include "log-core.h"
+
+#define SYSTEM_NAME "SC-Irdeto"
+#define SYSTEM_PRI -5
+
+#define SC_NAME "Irdeto"
+#define SC_ID MAKE_SC_ID('I','r','d','t')
+
+#define L_SC 8
+#define L_SC_ALL LALL(L_SC_LASTDEF)
+
+static const struct LogModule lm_sc = {
+ (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+ "sc-irdeto",
+ { L_SC_DEFNAMES }
+ };
+ADD_MODULE(L_SC,lm_sc)
+
+static void BN_complement(const unsigned char *data, int len, BIGNUM *bn)
+{
+ unsigned char buff[len];
+ for(int i=len-1; i>=0; i--) buff[i]=~data[i];
+ BN_bin2bn(buff,len,bn);
+ BN_add_word(bn,1);
+}
+
+// -- cSystemScIrdeto ----------------------------------------------------------
+
+class cSystemScIrdeto : public cSystemScCore {
+public:
+ cSystemScIrdeto(void);
+ };
+
+cSystemScIrdeto::cSystemScIrdeto(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Irdeto")
+{
+ hasLogger=true;
+}
+
+// -- cSystemLinkScIrdeto ------------------------------------------------------
+
+class cSystemLinkScIrdeto : public cSystemLink {
+public:
+ cSystemLinkScIrdeto(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemScIrdeto; }
+ };
+
+static cSystemLinkScIrdeto staticInit;
+
+cSystemLinkScIrdeto::cSystemLinkScIrdeto(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScIrdeto::CanHandle(unsigned short SysId)
+{
+ bool res=false;
+ cSmartCard *card=smartcards.LockCard(SC_ID);
+ if(card) {
+ res=card->CanHandle(SysId);
+ smartcards.ReleaseCard(card);
+ }
+ return res;
+}
+
+// -- cCamCrypt ----------------------------------------------------------------
+
+class cCamCrypt {
+private:
+ static const unsigned char cryptTable[];
+ bool randomInit;
+ cRSA rsa;
+ cBN cardExp, cardMod;
+ //
+ void GenerateRandom(unsigned char *buf, int len);
+ void RotateRight8Byte(unsigned char *key);
+ void RotateLeft8Byte(unsigned char *key);
+protected:
+ void CamCrypt(const unsigned char *key, unsigned char *data);
+ void RevCamCrypt(const unsigned char *key, unsigned char *data);
+ //
+ bool SetupCardFiles(unsigned char *data, int len, BIGNUM *exp, BIGNUM *mod);
+ void PrepareCamMessage(unsigned char *plain);
+ bool EncryptCamMessage(unsigned char *encrypted, const unsigned char *plain);
+ const unsigned char *CamKey(const unsigned char *data) { return data+8; }
+ const unsigned char *HelperKey(const unsigned char *data) { return data+24; }
+public:
+ cCamCrypt(void);
+ };
+
+const unsigned char cCamCrypt::cryptTable[256] = {
+ 0xDA,0x26,0xE8,0x72,0x11,0x52,0x3E,0x46,0x32,0xFF,0x8C,0x1E,0xA7,0xBE,0x2C,0x29,
+ 0x5F,0x86,0x7E,0x75,0x0A,0x08,0xA5,0x21,0x61,0xFB,0x7A,0x58,0x60,0xF7,0x81,0x4F,
+ 0xE4,0xFC,0xDF,0xB1,0xBB,0x6A,0x02,0xB3,0x0B,0x6E,0x5D,0x5C,0xD5,0xCF,0xCA,0x2A,
+ 0x14,0xB7,0x90,0xF3,0xD9,0x37,0x3A,0x59,0x44,0x69,0xC9,0x78,0x30,0x16,0x39,0x9A,
+ 0x0D,0x05,0x1F,0x8B,0x5E,0xEE,0x1B,0xC4,0x76,0x43,0xBD,0xEB,0x42,0xEF,0xF9,0xD0,
+ 0x4D,0xE3,0xF4,0x57,0x56,0xA3,0x0F,0xA6,0x50,0xFD,0xDE,0xD2,0x80,0x4C,0xD3,0xCB,
+ 0xF8,0x49,0x8F,0x22,0x71,0x84,0x33,0xE0,0x47,0xC2,0x93,0xBC,0x7C,0x3B,0x9C,0x7D,
+ 0xEC,0xC3,0xF1,0x89,0xCE,0x98,0xA2,0xE1,0xC1,0xF2,0x27,0x12,0x01,0xEA,0xE5,0x9B,
+ 0x25,0x87,0x96,0x7B,0x34,0x45,0xAD,0xD1,0xB5,0xDB,0x83,0x55,0xB0,0x9E,0x19,0xD7,
+ 0x17,0xC6,0x35,0xD8,0xF0,0xAE,0xD4,0x2B,0x1D,0xA0,0x99,0x8A,0x15,0x00,0xAF,0x2D,
+ 0x09,0xA8,0xF5,0x6C,0xA1,0x63,0x67,0x51,0x3C,0xB2,0xC0,0xED,0x94,0x03,0x6F,0xBA,
+ 0x3F,0x4E,0x62,0x92,0x85,0xDD,0xAB,0xFE,0x10,0x2E,0x68,0x65,0xE7,0x04,0xF6,0x0C,
+ 0x20,0x1C,0xA9,0x53,0x40,0x77,0x2F,0xA4,0xFA,0x6D,0x73,0x28,0xE2,0xCD,0x79,0xC8,
+ 0x97,0x66,0x8E,0x82,0x74,0x06,0xC7,0x88,0x1A,0x4A,0x6B,0xCC,0x41,0xE9,0x9D,0xB8,
+ 0x23,0x9F,0x3D,0xBF,0x8D,0x95,0xC5,0x13,0xB9,0x24,0x5A,0xDC,0x64,0x18,0x38,0x91,
+ 0x7F,0x5B,0x70,0x54,0x07,0xB6,0x4B,0x0E,0x36,0xAC,0x31,0xE6,0xD6,0x48,0xAA,0xB4
+ };
+
+cCamCrypt::cCamCrypt(void)
+{
+ randomInit=false;
+}
+
+void cCamCrypt::GenerateRandom(unsigned char *randVal, int len)
+{
+ static const unsigned int seed = 0x9E3779B9;
+ if(!randomInit) {
+ RAND_seed(&seed,sizeof(seed));
+ randomInit=true;
+ }
+ RAND_bytes(randVal,len);
+}
+
+//
+// Rotates the 8 bytes bitwise right
+//
+void cCamCrypt::RotateRight8Byte(unsigned char *key)
+{
+ unsigned char t1=key[0];
+ for(int k=7 ; k>=0 ; k--) {
+ unsigned char t2=t1<<7;
+ t1=key[k]; key[k]=(t1>>1) | t2;
+ }
+}
+
+//
+// Rotates the 8 bytes bitwise left
+//
+void cCamCrypt::RotateLeft8Byte(unsigned char *key)
+{
+ unsigned char t1=key[7];
+ for(int k=0 ; k<8 ; k++) {
+ unsigned char t2=t1>>7;
+ t1=key[k]; key[k]=(t1<<1) | t2;
+ }
+}
+
+void cCamCrypt::RevCamCrypt(const unsigned char *key, unsigned char *data)
+{
+ unsigned char localKey[8];
+ memcpy(localKey,key,sizeof(localKey));
+ for(int idx1=0 ; idx1<8 ; idx1++) {
+ for(int idx2=0 ; idx2<8 ; idx2++) {
+ const unsigned char tmp1=cryptTable[data[7] ^ localKey[idx2] ^ idx1];
+ const unsigned char tmp2=data[0];
+ data[0]=data[1];
+ data[1]=data[2];
+ data[2]=data[3];
+ data[3]=data[4];
+ data[4]=data[5];
+ data[5]=data[6] ^ tmp1;
+ data[6]=data[7];
+ data[7]=tmp1 ^ tmp2 ;
+ }
+ RotateLeft8Byte(localKey);
+ }
+}
+
+void cCamCrypt::CamCrypt(const unsigned char *key, unsigned char *data)
+{
+ unsigned char localKey[8];
+ memcpy(localKey,key,sizeof(localKey));
+ for(int idx1=0 ; idx1<7 ; idx1++) RotateLeft8Byte(localKey);
+ for(int idx1=7 ; idx1>=0 ; idx1--) {
+ for(int idx2=7 ; idx2>=0 ; idx2--) {
+ const unsigned char tmp1=cryptTable[data[6] ^ localKey[idx2] ^ idx1];
+ const unsigned char tmp2=data[0];
+ data[0]=data[7] ^ tmp1;
+ data[7]=data[6];
+ data[6]=data[5] ^ tmp1;
+ data[5]=data[4];
+ data[4]=data[3];
+ data[3]=data[2];
+ data[2]=data[1];
+ data[1]=tmp2;
+ }
+ RotateRight8Byte(localKey);
+ }
+}
+
+bool cCamCrypt::SetupCardFiles(unsigned char *data, int len, BIGNUM *exp, BIGNUM *mod)
+{
+ if(rsa.RSA(data,data,len,exp,mod,false)<=0 || (data[0]!=0x80 || data[11]!=0x40 || data[12]!=0x06))
+ return false;
+ BN_complement(data+13,64,cardMod);
+ BN_bin2bn(data+13+64,6,cardExp);
+ return true;
+}
+
+void cCamCrypt::PrepareCamMessage(unsigned char *plain)
+{
+/*
+ static const unsigned char camMsg[] = {
+ 0x80,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,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00
+ };
+ memcpy(plain,camMsg,sizeof(camMsg));
+*/
+ plain[0]=0x80;
+ memset(plain+1 ,0x00,7);
+ GenerateRandom(plain+16,16);
+ memcpy(plain+8,plain+16,8);
+ memset(plain+32,0xFF,31);
+ plain[63]=0x00;
+}
+
+bool cCamCrypt::EncryptCamMessage(unsigned char *encrypted, const unsigned char *plain)
+{
+ return rsa.RSA(encrypted,plain,64,cardExp,cardMod,false)>0;
+}
+
+// -- cSmartCardDataIrdeto -----------------------------------------------------
+
+class cSmartCardDataIrdeto : public cSmartCardData {
+public:
+ int acs, caid;
+ cBN mod, exp;
+ bool plain;
+ //
+ cSmartCardDataIrdeto(void);
+ cSmartCardDataIrdeto(int Acs, int Caid);
+ virtual bool Parse(const char *line);
+ virtual bool Matches(cSmartCardData *param);
+ };
+
+cSmartCardDataIrdeto::cSmartCardDataIrdeto(void)
+:cSmartCardData(SC_ID)
+{
+ plain=false;
+}
+
+cSmartCardDataIrdeto::cSmartCardDataIrdeto(int Acs, int Caid)
+:cSmartCardData(SC_ID)
+{
+ acs=Acs; caid=Caid;
+ plain=false;
+}
+
+bool cSmartCardDataIrdeto::Matches(cSmartCardData *param)
+{
+ cSmartCardDataIrdeto *cd=(cSmartCardDataIrdeto *)param;
+ return cd->acs==acs && (cd->caid==caid || caid==-1);
+}
+
+bool cSmartCardDataIrdeto::Parse(const char *line)
+{
+ unsigned char buff[512];
+ acs=caid=-1; // default
+ line=skipspace(line);
+ if(*line=='[') { // parse acs & caid
+ line++;
+ if(GetHex(line,buff,2)!=2) {
+ PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: acs");
+ return false;
+ }
+ acs=buff[0]*256+buff[1];
+
+ line=skipspace(line);
+ if(*line=='/') {
+ line++;
+ if(GetHex(line,buff,2)!=2) {
+ PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: caid");
+ return false;
+ }
+ caid=buff[0]*256+buff[1];
+ line=skipspace(line);
+ }
+
+ if(!*line==']') {
+ PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: closing ]");
+ return false;
+ }
+ line++;
+ }
+
+ line=skipspace(line);
+ if(!strncasecmp(line,"plain",5)) {
+ plain=true;
+ return true;
+ }
+ int l;
+ if((l=GetHex(line,buff,sizeof(buff),false))<=0) {
+ PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: mod");
+ return false;
+ }
+ BN_complement(buff,l,mod);
+ if((l=GetHex(line,buff,sizeof(buff),false))<=0) {
+ PRINTF(L_CORE_LOAD,"smartcarddatairdeto: format error: exp");
+ return false;
+ }
+ BN_bin2bn(buff,l,exp);
+ return true;
+}
+
+// -- cSmartCardIrdeto ---------------------------------------------------------
+
+#define XOR_START 0x3F // Start value for xor checksumm
+#define ADDRLEN 4 // Address length in EMM commands
+#define MAX_PROV 16
+#define RECOVER_TIME 100 // Time in ms which the card needs to recover after
+ // a failed command
+
+class cSmartCardIrdeto : public cSmartCard, private cCamCrypt, private cIdSet {
+private:
+ unsigned char buff[MAX_LEN+1];
+ unsigned char camKey[8];
+ char asciiSerial[22], coco[4];
+ int ACS, caId;
+ int numProv;
+ cTimeMs recoverTime;
+ //
+ int DoCmd(unsigned char *cmd, int goodSB, int secGoodSB=-1);
+ bool ReadCardInfo(void);
+ time_t Date(int date, char *buff, int len);
+public:
+ cSmartCardIrdeto(void);
+ virtual bool Init(void);
+ virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+ virtual bool Update(int pid, int caid, const unsigned char *data);
+ virtual bool CanHandle(unsigned short CaId);
+ };
+
+static const struct StatusMsg msgs[] = {
+ { { 0x00,0x00 }, "Instruction executed without error", true },
+ { { 0x55,0x00 }, "Instruction executed without error", true },
+ { { 0x57,0x00 }, "CAM string rejected", false },
+ { { 0x58,0x00 }, "Instruction executed without error", true },
+ { { 0x9D,0x00 }, "Decoding successfull", true },
+ { { 0x90,0x00 }, "ChID missing. Not subscribed?", false },
+ { { 0x93,0x00 }, "ChID out of date. Subscription expired?", false },
+ { { 0x9C,0x00 }, "Master key error", false },
+ { { 0x9E,0x00 }, "Wrong decryption key", false },
+ { { 0x9F,0x00 }, "Missing key", false },
+ { { 0x70,0x00 }, "Wrong hex serial", false },
+ { { 0x71,0x00 }, "Wrong provider", false },
+ { { 0x72,0x00 }, "Wrong provider group", false },
+ { { 0x73,0x00 }, "Wrong provider group", false },
+ { { 0x7C,0x00 }, "Wrong signature", false },
+ { { 0x7D,0x00 }, "Masterkey missing", false },
+ { { 0x7E,0x00 }, "Wrong provider identifier", false },
+ { { 0x7F,0x00 }, "Invalid nano", false },
+ { { 0x54,0x00 }, "No more ChID's", true },
+ { { 0xFF,0xFF }, 0, false }
+ };
+
+static const struct CardConfig cardCfg = {
+ SM_8N2,3000,100
+ };
+
+cSmartCardIrdeto::cSmartCardIrdeto(void)
+:cSmartCard(&cardCfg,msgs)
+{}
+
+bool cSmartCardIrdeto::Init(void)
+{
+ ResetIdSet();
+ recoverTime.Set(-RECOVER_TIME);
+ if(atr->histLen<6 || memcmp(atr->hist,"IRDETO",6)) {
+ PRINTF(L_SC_INIT,"doesn't looks like a Irdeto/Beta card");
+ return false;
+ }
+
+ infoStr.Begin();
+ infoStr.Strcat("Irdeto smartcard\n");
+ int r;
+ static unsigned char getCountryCode[] = { 0x01,0x02,0x02,0x03,0x00,0x00,0xCC };
+ if((r=DoCmd(getCountryCode,0x0000))<=0 || !Status() || r<16) {
+ PRINTF(L_SC_ERROR,"country code error");
+ return false;
+ }
+ ACS=buff[8]*256+buff[9];
+ caId=buff[13]*256+buff[14];
+ memcpy(coco,&buff[21],3); coco[3]=0;
+ PRINTF(L_SC_INIT,"ACS Version %04x, CAID %04x, CoCo %s",ACS,caId,coco);
+ snprintf(idStr,sizeof(idStr),"%s (ACS %x)",SC_NAME,ACS);
+ infoStr.Printf("ACS: %04x CAID: %04x CoCo: %s\n",ACS,caId,coco);
+
+ static unsigned char getAsciiSerial[] = { 0x01,0x02,0x00,0x03,0x00,0x00,0xCC };
+ if((r=DoCmd(getAsciiSerial,0x0000))<=0 || !Status() || r<10) {
+ PRINTF(L_SC_ERROR,"ASCII serial error");
+ return false;
+ }
+ strn0cpy(asciiSerial,(char*)buff+8,sizeof(asciiSerial));
+ PRINTF(L_SC_INIT,"ASCII serial %s",asciiSerial);
+
+ static unsigned char getHexSerial[] = { 0x01,0x02,0x01,0x03,0x00,0x00,0xCC };
+ if((r=DoCmd(getHexSerial,0x0000))<=0 || !Status() || r<25) {
+ PRINTF(L_SC_ERROR,"hex serial error");
+ return false;
+ }
+ int numProv=buff[18];
+ SetCard(new cCardIrdeto(buff[23],&buff[20]));
+ PRINTF(L_SC_INIT,"Providers: %d HEX Serial: %02X%02X%02X HEX Base: %02X",numProv,buff[20],buff[21],buff[22],buff[23]);
+ infoStr.Printf("HEX: %02X/%02X%02X%02X ASCII: %s\n",buff[23],buff[20],buff[21],buff[22],asciiSerial);
+
+ static unsigned char getCardFile[] = { 0x01,0x02,0x0E,0x02,0x00,0x00,0xCC };
+ unsigned char encr[128], plain[64+6+2];
+ PrepareCamMessage(plain+6);
+ getCardFile[3]=0x02;
+ if((r=DoCmd(getCardFile,0x0000))<=0 || !Status() || r<73) {
+ PRINTF(L_SC_ERROR,"cardfile2 error");
+ return false;
+ }
+ memcpy(encr,buff+8,64);
+ getCardFile[3]=0x03;
+ if((r=DoCmd(getCardFile,0x0000))<=0 || !Status() || r<73) {
+ PRINTF(L_SC_ERROR,"cardfile3 error");
+ return false;
+ }
+ memcpy(encr+64,buff+8,64);
+
+ bool doPlain=false;
+ if((ACS==0x0383 || ACS==0x0384) && atr->histLen>=12 && atr->hist[12]==0x95)
+ doPlain=true;
+ cSmartCardDataIrdeto *entry=0;
+ if(!doPlain) {
+ cSmartCardDataIrdeto cd(ACS,caId);
+ if(!(entry=(cSmartCardDataIrdeto *)smartcards.FindCardData(&cd))) {
+ PRINTF(L_GEN_WARN,"didn't find Irdeto card specific certificate, falling back to default");
+ cSmartCardDataIrdeto cd(-1,-1);
+ if(!(entry=(cSmartCardDataIrdeto *)smartcards.FindCardData(&cd))) {
+ PRINTF(L_GEN_WARN,"didn't find default Irdeto certificate, please add one");
+ if(ACS!=0x0384) return false;
+ PRINTF(L_GEN_WARN,"trying pre-coded ACS 384 challenge. This mode is DEPRECATED. There ARE valid certificates for these cards available!");
+ }
+ }
+ else doPlain=entry->plain;
+ }
+ static unsigned char doCamKeyExchange[] = { 0x01,0x02,0x09,0x03,0x00,0x40 };
+ if(doPlain) {
+ // plain challenge
+ memcpy(plain,doCamKeyExchange,sizeof(doCamKeyExchange));
+ plain[4]=1; // set block counter
+ r=DoCmd(plain,0x5500,0x0000);
+ }
+ else {
+ // RSA challenge
+ if(entry) {
+ if(!SetupCardFiles(encr,sizeof(encr),entry->exp,entry->mod)) {
+ PRINTF(L_SC_ERROR,"decrypting cardfiles failed. Probably bad certificate.");
+ return false;
+ }
+ if(!EncryptCamMessage(encr+6,plain+6)) {
+ PRINTF(L_SC_ERROR,"encrypting CAM message failed. Probably bad certificate.");
+ return false;
+ }
+
+ static unsigned char doRSACheck[] = { 0x01,0x02,0x11,0x00,0x00,0x40 };
+ memcpy(plain,doRSACheck,sizeof(doRSACheck));
+ if((r=DoCmd(plain,0x5800,0x0000))<=0 || !Status() || r<73) {
+ PRINTF(L_SC_ERROR,"card didn't give a proper reply (buggy RSA unit?), trying to continue...");
+ // non-fatal
+ }
+ if(r==73 && memcmp(encr+6,buff+8,64)) {
+ PRINTF(L_SC_ERROR,"card failed on RSA check, trying to continue...");
+ // non-fatal
+ }
+ }
+ else {
+ static const unsigned char enc384cz[] = {
+ 0x18,0xD7,0x55,0x14,0xC0,0x83,0xF1,0x38, 0x39,0x6F,0xF2,0xEC,0x4F,0xE3,0xF1,0x85,
+ 0x01,0x46,0x06,0xCE,0x7D,0x08,0x2C,0x74, 0x46,0x8F,0x72,0xC4,0xEA,0xD7,0x9C,0xE0,
+ 0xE1,0xFF,0x58,0xE7,0x70,0x0C,0x92,0x45, 0x26,0x18,0x4F,0xA0,0xE2,0xF5,0x9E,0x46,
+ 0x6F,0xAE,0x95,0x35,0xB0,0x49,0xB2,0x0E, 0xA4,0x1F,0x8E,0x47,0xD0,0x24,0x11,0xD0
+ };
+ static const unsigned char enc384dz[] = {
+ 0x27,0xF2,0xD6,0xCD,0xE6,0x88,0x62,0x46, 0x81,0xB0,0xF5,0x3E,0x6F,0x13,0x4D,0xCC,
+ 0xFE,0xD0,0x67,0xB1,0x93,0xDD,0xF4,0xDE, 0xEF,0xF5,0x3B,0x04,0x1D,0xE5,0xC3,0xB2,
+ 0x54,0x38,0x57,0x7E,0xC8,0x39,0x07,0x2E, 0xD2,0xF4,0x05,0xAA,0x15,0xB5,0x55,0x24,
+ 0x90,0xBB,0x9B,0x00,0x96,0xF0,0xCB,0xF1, 0x8A,0x08,0x7F,0x0B,0xB8,0x79,0xC3,0x5D
+ };
+ static const unsigned char ck[] = { 0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88 };
+ static const unsigned char hk[] = { 0x12,0x34,0x56,0x78,0x90,0xAB,0xCD,0xEF };
+
+ if(caId==0x1702) memcpy(encr+6,enc384cz,sizeof(enc384cz));
+ else if(caId==0x1722) memcpy(encr+6,enc384dz,sizeof(enc384dz));
+ else {
+ PRINTF(L_GEN_WARN,"no pre-coded Irdeto camkey challenge for caid %04x",caId);
+ return false;
+ }
+ memcpy((void *)CamKey(plain+6),ck,sizeof(ck));
+ memcpy((void *)HelperKey(plain+6),hk,sizeof(hk));
+ }
+
+ memcpy(encr,doCamKeyExchange,sizeof(doCamKeyExchange));
+ r=DoCmd(encr,0x5500,0x0000);
+ }
+
+ if(r>0 && Status() && r>=9) {
+ memcpy(camKey,CamKey(plain+6),8);
+ if(r>=17) {
+ RevCamCrypt(camKey,buff+8);
+ if(memcmp(HelperKey(plain+6),buff+8,8)) {
+ PRINTF(L_SC_ERROR,"camkey challenge failed");
+ return false;
+ }
+ }
+ LDUMP(L_SC_INIT,camKey,sizeof(camKey),"camkey");
+ }
+ else {
+ PRINTF(L_SC_ERROR,"camkey error");
+ return false;
+ }
+
+ static unsigned char getProvider[] = { 0x01,0x02,0x03,0x03,0x00,0x00,0xCC };
+ static unsigned char getChanelIds[] = { 0x01,0x02,0x04,0x00,0x00,0x01,0x00,0xCC };
+ for(int i=0; i<numProv; i++) {
+ getProvider[4]=i;
+ if((r=DoCmd(getProvider,0x0000))>0 && Status() && r>=33) {
+ AddProv(new cProviderIrdeto(buff[8]&0x0f,&buff[9]));
+ PRINTF(L_SC_INIT,"provider %d with ProvBase 0x%02x ProvId 0x%02x%02x%02x",i,buff[8]&0x0f,buff[9],buff[10],buff[11]);
+ infoStr.Printf("Provider %d Id: %02X/%02X%02X%02X\n",i,buff[8]&0x0f,buff[9],buff[10],buff[11]);
+
+ getChanelIds[4]=i;
+ for(int l=0; l<10; l++) {
+ getChanelIds[6]=l;
+ if((r=DoCmd(getChanelIds,0x0000,0x5400))<=0 || !Status() || r<69) break;
+ for(int k=0; k<buff[7]; k+=6) {
+ int chanId=buff[k+8+0]*256+buff[k+8+1];
+ int date =buff[k+8+2]*256+buff[k+8+3];
+ int durr =buff[k+8+4];
+ if(chanId!=0x0000 && chanId!=0xFFFF) {
+ char sdate[16], edate[16];
+ Date(date,sdate,sizeof(sdate));
+ Date(date+durr,edate,sizeof(edate));
+ PRINTF(L_SC_INIT,"ChanId 0x%04x Date 0x%04x %s Duration 0x%02x %s",chanId,date,sdate,durr,edate);
+ infoStr.Printf("ChanId: %04X Date: %s-%s\n",chanId,sdate,edate);
+ }
+ }
+ }
+ }
+ }
+
+#if 0
+ static unsigned char getCountryCode2[] = { 0x01,0x02,0x0B,0x00,0x00,0x00,0xCC };
+ if((r=DoCmd(getCountryCode2,0x0000))>0 && Status() && r>=32) {
+ PRINTF(L_SC_INIT,"max ChID's %d,%d,%d,%d",buff[14],buff[15],buff[16],buff[17]);
+ }
+#endif
+
+ infoStr.Finish();
+ return true;
+}
+
+time_t cSmartCardIrdeto::Date(int date, char *buff, int len)
+{
+ // Irdeto date starts 01.08.1997 which is
+ // 870393600 seconds in unix calendar time
+ time_t utcTime=870393600L+date*(24*3600);
+ if(buff) {
+ struct tm utcTm;
+ gmtime_r(&utcTime,&utcTm);
+ snprintf(buff,len,"%04d/%02d/%02d",utcTm.tm_year+1900,utcTm.tm_mon+1,utcTm.tm_mday);
+ }
+ return utcTime;
+}
+
+bool cSmartCardIrdeto::CanHandle(unsigned short CaId)
+{
+ return (CaId==caId);
+}
+
+bool cSmartCardIrdeto::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+ static const unsigned char ecmCmd[] = { 0x01,0x05,0x00,0x00,0x02,0x00 };
+
+ int r=SCT_LEN(data)-6;
+ if(r<=255) {
+ unsigned char cmd[257+sizeof(ecmCmd)];
+ memcpy(cmd,ecmCmd,sizeof(ecmCmd));
+ cmd[5]=r;
+ memcpy(cmd+sizeof(ecmCmd),&data[6],r);
+ if((r=DoCmd(cmd,0x9D00))>0) {
+ if(Status() && r>=31) {
+ RevCamCrypt(camKey,&buff[14]);
+ RevCamCrypt(camKey,&buff[22]);
+ memcpy(cw,&buff[14],16);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool cSmartCardIrdeto::Update(int pid, int caid, const unsigned char *data)
+{
+ static const unsigned char emmCmd[] = { 0x01,0x01,0x00,0x00,0x00,0x00 };
+
+ if(MatchEMM(data)) {
+ int len=cParseIrdeto::AddrLen(data)+1;
+ if(len<=ADDRLEN) {
+ const int dataLen=SCT_LEN(data)-5-len; // sizeof of data bytes (nanos)
+ if(dataLen<=255-ADDRLEN) {
+ unsigned char cmd[257+sizeof(emmCmd)];
+ memcpy(cmd,emmCmd,sizeof(emmCmd));
+ cmd[5]=dataLen+ADDRLEN;
+ memset(cmd+sizeof(emmCmd),0,ADDRLEN);
+ memcpy(cmd+sizeof(emmCmd),&data[3],len);
+// if(data[len+3]==0x01 && data[len+4]==0x00) {
+ memcpy(cmd+sizeof(emmCmd)+ADDRLEN,&data[len+5],dataLen);
+ if(DoCmd(cmd,0x0000)>0 && Status()) return true;
+// }
+// else d(printf("smartcardirdeto: bad EMM format, 0x0100 marker is 0x%02x%02x\n",data[len+3],data[len+4]))
+ }
+ }
+ else PRINTF(L_SC_ERROR,"addrlen %d > %d",len,ADDRLEN);
+ }
+ return false;
+}
+
+int cSmartCardIrdeto::DoCmd(unsigned char *cmd, int goodSB, int secGoodSB)
+{
+ int len=cmd[5]+6;
+ cmd[len]=XorSum(cmd,len) ^ XOR_START;
+ // wait until recover time is over
+ int r=RECOVER_TIME-recoverTime.Elapsed();
+ if(r>0) {
+ PRINTF(L_SC_ERROR,"recover time, waiting %d ms",r);
+ cCondWait::SleepMs(r+1);
+ }
+ r=-1;
+ LDUMP(L_CORE_SC,cmd,len+1,"IRDETO: CMD ->");
+ if(SerWrite(cmd,len+1)>0 && SerRead(buff,4,cardCfg.workTO)>0) {
+ len=4;
+ if(buff[0]==cmd[0] && buff[1]==cmd[1]) {
+ sb[0]=buff[2]; sb[1]=buff[3];
+ int SB=buff[2]*256+buff[3];
+ if(SB==goodSB || (secGoodSB>=0 && SB==secGoodSB)) {
+ if(SerRead(buff+len,5)>0) {
+ len+=5;
+ if(buff[7]) {
+ if(SerRead(buff+len,buff[7])<=0) return -1;
+ len+=buff[7];
+ }
+ if(XorSum(buff,len)==XOR_START) r=len;
+ else LDUMP(L_CORE_SC,buff,len,"IRDETO: checksum failed");
+ }
+ }
+ else r=len;
+ }
+ else {
+ sb[0]=buff[1]; sb[1]=buff[2];
+ r=3;
+ }
+ }
+ if(r>0) LDUMP(L_CORE_SC,buff,r,"IRDETO: RESP <-");
+ if(r<=4) {
+ recoverTime.Set();
+ PRINTF(L_SC_ERROR,"setting %d ms recover time",RECOVER_TIME);
+ }
+ return r;
+}
+
+// -- cSmartCardLinkIrdeto -----------------------------------------------------
+
+class cSmartCardLinkIrdeto : public cSmartCardLink {
+public:
+ cSmartCardLinkIrdeto(void):cSmartCardLink(SC_NAME,SC_ID) {}
+ virtual cSmartCard *Create(void) { return new cSmartCardIrdeto(); }
+ virtual cSmartCardData *CreateData(void) { return new cSmartCardDataIrdeto; }
+ };
+
+static cSmartCardLinkIrdeto staticScInit;
--- /dev/null
+#
+# Smartcard Irdeto/Beta
+#
+TARGET = sc_irdeto
+OBJS = sc-irdeto.o
+LIBS = -lcrypto
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <openssl/rand.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "crypto.h"
+#include "helper.h"
+#include "log-sc.h"
+#include "log-core.h"
+
+#define SYSTEM_NAGRA 0x1801
+
+#define SYSTEM_NAME "SC-Nagra"
+#define SYSTEM_PRI -5
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_NAGRA)
+
+#define SC_NAME "Nagra"
+#define SC_ID MAKE_SC_ID('N','a','g','r')
+
+#define L_SC 11
+#define L_SC_PROC LCLASS(L_SC,L_SC_LASTDEF<<1)
+#define L_SC_ALL LALL(L_SC_PROC)
+
+static const struct LogModule lm_sc = {
+ (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SC_DEFDEF|L_SC_PROC)&LOPT_MASK,
+ "sc-nagra",
+ { L_SC_DEFNAMES,"process" }
+ };
+ADD_MODULE(L_SC,lm_sc)
+
+// -- cSystemScNagra ---------------------------------------------------------------------
+
+class cSystemScNagra : public cSystemScCore {
+public:
+ cSystemScNagra(void);
+ };
+
+cSystemScNagra::cSystemScNagra(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Nagra")
+{
+ hasLogger=true;
+}
+
+// -- cSystemLinkScNagra --------------------------------------------------------------
+
+class cSystemLinkScNagra : public cSystemLink {
+public:
+ cSystemLinkScNagra(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemScNagra; }
+ };
+
+static cSystemLinkScNagra staticInit;
+
+cSystemLinkScNagra::cSystemLinkScNagra(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScNagra::CanHandle(unsigned short SysId)
+{
+ return smartcards.HaveCard(SC_ID) && SYSTEM_CAN_HANDLE(SysId);
+}
+
+// -- cCamCryptNagra ------------------------------------------------------------------
+
+class cCamCryptNagra {
+private:
+ cIDEA idea;
+ cRSA rsa;
+ cBN camMod, camExp;
+ bool hasMod;
+ unsigned char sk[16];
+ //
+ void Signature(unsigned char *sig, const unsigned char *key, const unsigned char *msg, int len);
+public:
+ cCamCryptNagra(void);
+ void InitCamKey(unsigned char *key, const unsigned char *bk, const unsigned char *data);
+ void SetCamMod(BIGNUM *m);
+ bool DecryptCamMod(const unsigned char *dt08, const unsigned char *key, const unsigned char *key2, BIGNUM *m);
+ bool DecryptCamData(unsigned char *out, const unsigned char *key, const unsigned char *in);
+ bool DecryptCW(unsigned char *dcw, const unsigned char *in);
+ };
+
+cCamCryptNagra::cCamCryptNagra(void)
+{
+ hasMod=false;
+ BN_set_word(camExp,3);
+}
+
+void cCamCryptNagra::Signature(unsigned char *sig, const unsigned char *key, const unsigned char *msg, int len)
+{
+ unsigned char buff[16];
+ memcpy(buff,key,16);
+ for(int i=0; i<len; i+=8) {
+ IdeaKS ks;
+ idea.SetEncKey(buff,&ks);
+ idea.Encrypt(msg+i,8,buff,&ks,0);
+ xxor(buff,8,buff,&msg[i]);
+ //for(int j=7; j>=0; j--) buff[j]^=msg[i+j];
+ memcpy(&buff[8],buff,8);
+ }
+ memcpy(sig,buff,8);
+}
+
+void cCamCryptNagra::InitCamKey(unsigned char *key, const unsigned char *bk, const unsigned char *data)
+{
+ memcpy(key,bk,8);
+ int d=UINT32_LE(data);
+ BYTE4_LE(key+8 , d);
+ BYTE4_LE(key+12,~d);
+ //memcpy(key+8,data,4);
+ //for(int i=0; i<4; i++) key[i+12]=~data[i];
+}
+
+void cCamCryptNagra::SetCamMod(BIGNUM *m)
+{
+ BN_copy(camMod,m);
+ hasMod=true;
+}
+
+bool cCamCryptNagra::DecryptCamMod(const unsigned char *dt08, const unsigned char *key, const unsigned char *cardid, BIGNUM *m)
+{
+ unsigned char buff[72];
+ if(rsa.RSA(buff,dt08+13,64,camExp,m)!=64) return false;
+ memcpy(buff+64,dt08+13+64,8);
+ buff[63]|=dt08[12]&0x80;
+ IdeaKS dks;
+ idea.SetDecKey(key,&dks);
+ idea.Decrypt(buff,72,&dks,0);
+ unsigned char sig[8];
+ memcpy(sig,buff,8);
+
+ memset(buff+0,0,4);
+ memcpy(buff+4,cardid,4);
+ Signature(buff,key,buff,72);
+ BN_bin2bn(buff+8,64,camMod);
+ if(memcmp(sig,buff,8)) {
+ PRINTF(L_SC_PROC,"DT08 signature failed. Check boxkey/IRD modulus");
+ return false;
+ }
+ hasMod=true;
+ return true;
+}
+
+bool cCamCryptNagra::DecryptCamData(unsigned char *out, const unsigned char *key, const unsigned char *camdata)
+{
+ if(!hasMod) return false;
+ //decrypt $2A data here and prepare $2B reply
+ if(rsa.RSA(out,camdata,64,camExp,camMod)!=64) return false;
+ Signature(sk,key,out,32);
+ memcpy(sk+8,sk,8);
+ Signature(sk+8,sk,out,32);;
+ if(rsa.RSA(out,out,64,camExp,camMod)!=64) return false;
+ LDUMP(L_SC_PROC,sk,16,"established session key ->");
+ return true;
+}
+
+bool cCamCryptNagra::DecryptCW(unsigned char *dcw, const unsigned char *ecw)
+{
+ const int nCw=ecw[4]/2;
+ if(nCw<10) return false;
+ IdeaKS ks;
+ ecw+=5;
+ idea.SetDecKey(sk,&ks);
+ memcpy(dcw+8,ecw +2,8);
+ memcpy(dcw+0,ecw+nCw+2,8);
+ idea.Decrypt(dcw+0,8,&ks,0);
+ idea.Decrypt(dcw+8,8,&ks,0);
+ return true;
+}
+
+// -- cSmartCardDataNagra ------------------------------------------------------
+
+class cSmartCardDataNagra : public cSmartCardData {
+public:
+ bool global;
+ int provId;
+ unsigned char bk[8];
+ cBN mod;
+ //
+ cSmartCardDataNagra(void);
+ cSmartCardDataNagra(int ProvId, bool Global);
+ virtual bool Parse(const char *line);
+ virtual bool Matches(cSmartCardData *param);
+ };
+
+cSmartCardDataNagra::cSmartCardDataNagra(void)
+:cSmartCardData(SC_ID) {}
+
+cSmartCardDataNagra::cSmartCardDataNagra(int ProvId, bool Global=false)
+:cSmartCardData(SC_ID)
+{
+ provId=ProvId; global=Global;
+}
+
+bool cSmartCardDataNagra::Matches(cSmartCardData *param)
+{
+ cSmartCardDataNagra *cd=(cSmartCardDataNagra *)param;
+ return (cd->provId==provId) && (cd->global==global);
+}
+
+bool cSmartCardDataNagra::Parse(const char *line)
+{
+ unsigned char buff[512];
+ line=skipspace(line);
+ if(*line++!='[' || GetHex(line,buff,2)!=2 || *(line=skipspace(line))!=']' ) {
+ PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: provider id");
+ return false;
+ }
+ provId=buff[0]*256+buff[1];
+ line=skipspace(line+1);
+ if(!strncasecmp(line,"IRDMOD",6)) {
+ global=true;
+ line+=6;
+ }
+ if(GetHex(line,bk,sizeof(bk),false)<=0) {
+ PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: boxkey");
+ return false;
+ }
+ int l=GetHex(line,buff,sizeof(buff),false);
+ if(l!=64) {
+ PRINTF(L_CORE_LOAD,"smartcarddatanagra: format error: %s",global?"IRDMOD":"CAMMOD");
+ return false;
+ }
+ BN_bin2bn(buff,l,mod);
+ return true;
+}
+
+// -- cSmartCardNagra -----------------------------------------------------------------
+
+// ISO 7816 T=1 defines
+#define NAD(a) (a[0])
+#define PCB(a) (a[1])
+#define LEN(a) (a[2])
+#define CLA(a) (a[3])
+#define INS(a) (a[4])
+#define P1(a) (a[5])
+#define P2(a) (a[6])
+#define L(a) (a[7])
+
+// Nagra NAD and reply NAD
+#define N2_NAD 0x21
+#define N2_R_NAD sn8(N2_NAD)
+#define N2_CLA 0xA0
+#define N2_INS 0xCA
+#define N2_P1 0x00
+#define N2_P2 0x00
+#define N2_CMD(a) (a[8])
+#define N2_CLEN(a) (a[9])
+#define N2_DATA(a) (a[10])
+
+#define SETIFS 0xC1
+#define CAMDATA 0x08
+
+// Datatypes
+#define IRDINFO 0x00
+#define TIERS 0x05
+#define MAX_REC 20
+
+// Card Status checks
+#define HAS_CW ((status[1]&1)&&((status[3]&6)==6))
+
+class cSmartCardNagra : public cSmartCard, private cCamCryptNagra {
+private:
+ unsigned char buff[MAX_LEN+1], status[4], cardId[4], irdId[4], block;
+ unsigned short provId[MAX_REC], irdProvId;
+ //
+ bool GetCardStatus(void);
+ bool GetDataType(unsigned char dt, int len, int shots=MAX_REC);
+ bool ParseDataType(unsigned char dt);
+ bool DoCamExchange(const unsigned char *key);
+ void Date(const unsigned char *date, char *dt, char *ti);
+ bool DoBlkCmd(unsigned char cmd, int ilen, unsigned char res, int rlen, const unsigned char *data=0);
+ bool SetIFS(unsigned char len);
+public:
+ cSmartCardNagra(void);
+ virtual bool Init(void);
+ virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+ virtual bool Update(int pid, int caid, const unsigned char *data);
+ };
+
+static const struct StatusMsg msgs[] = {
+ { { 0x6F,0x00 }, "Instruction not supported", false },
+ { { 0x90,0x00 }, "Instruction executed without errors", true },
+ { { 0xFF,0xFF }, 0, false }
+ };
+
+static const struct CardConfig cardCfg = {
+ SM_8O2,500,800
+ };
+
+cSmartCardNagra::cSmartCardNagra(void)
+:cSmartCard(&cardCfg,msgs)
+{
+ block=0;
+}
+
+bool cSmartCardNagra::SetIFS(unsigned char size)
+{
+ unsigned char buf[5];
+ // NAD, PCB, LEN
+ NAD(buf)=N2_NAD; PCB(buf)=SETIFS; LEN(buf)=1;
+ // Information Field size
+ buf[3]=size; buf[4]=XorSum(buf,4);
+ if(SerWrite(buf,5)!=5) {
+ PRINTF(L_SC_ERROR,"failed to write IFS command");
+ return false;
+ }
+ if(SerRead(buff,5,cardCfg.workTO)!=5 || XorSum(buff,5)) {
+ PRINTF(L_SC_ERROR,"setting IFS to %02x failed",size);
+ return false;
+ }
+ return true;
+}
+
+bool cSmartCardNagra::GetCardStatus(void)
+{
+ if(!DoBlkCmd(0xC0,0x02,0xB0,0x06) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to read card status");
+ return false;
+ }
+ memcpy(status,buff+5,4);
+ return true;
+}
+
+bool cSmartCardNagra::Init(void)
+{
+ static const unsigned char verifyBytes[] = { 'D','N','A','S','P','1' };
+ if(atr->T!=1 || (atr->histLen<8 && memcmp(atr->hist,verifyBytes,sizeof(verifyBytes)))) {
+ PRINTF(L_SC_INIT,"doesn't look like a nagra V2 card");
+ return false;
+ }
+
+ infoStr.Begin();
+ infoStr.Strcat("Nagra smartcard\n");
+
+ char rom[12], rev[12];
+ snprintf(rom,sizeof(rom),"%.3s",(char*)&atr->hist[5]);
+ snprintf(rev,sizeof(rev),"%.3s",(char*)&atr->hist[12]);
+
+ PRINTF(L_SC_INIT,"card version: %s revision: %s",rom,rev);
+ infoStr.Printf("Card v.%s Rev.%s\n",rom,rev);
+
+ if(!GetCardStatus() || !SetIFS(0x80)) return false;
+ // get UA/CardID here
+ if(!DoBlkCmd(0x12,0x02,0x92,0x06) || !Status()) return false;
+
+ memcpy(cardId,buff+5,4);
+ if(!GetDataType(IRDINFO,0x39) || !GetDataType(0x04,0x44)) return false;
+ infoStr.Printf("Tiers\n");
+ infoStr.Printf("|id |tier low|tier high| dates |\n");
+ infoStr.Printf("+----+--------+---------+----------+\n");
+
+ if(!GetDataType(TIERS,0x57))
+ PRINTF(L_SC_ERROR,"failed to get tiers");
+
+ if(!GetDataType(CAMDATA,0x55,1)) return false;
+
+ unsigned char key[16];
+ cSmartCardDataNagra *entry=0;
+ bool sessOk=false;
+ if(buff[5]!=0 && irdProvId==((buff[10]*256)|buff[11])) { // prepare DT08 data
+ cSmartCardDataNagra cd(irdProvId,true);
+ if(!(entry=(cSmartCardDataNagra *)smartcards.FindCardData(&cd))) {
+ PRINTF(L_GEN_WARN,"didn't find smartcard Nagra IRD modulus");
+ }
+ else {
+ InitCamKey(key,entry->bk,irdId);
+ if(DecryptCamMod(buff+3,key,cardId,entry->mod)) sessOk=true;
+ }
+ }
+ else {
+ cSmartCardDataNagra cd(irdProvId);
+ if(!(entry=(cSmartCardDataNagra *)smartcards.FindCardData(&cd))) {
+ PRINTF(L_GEN_WARN,"didn't find smartcard Nagra CAM modulus");
+ }
+ else {
+ InitCamKey(key,entry->bk,cardId);
+ SetCamMod(entry->mod);
+ if(GetDataType(0x08,0x03,1)) sessOk=true;;
+ }
+ }
+ if(sessOk) DoCamExchange(key);
+ infoStr.Finish();
+ return true;
+}
+
+bool cSmartCardNagra::GetDataType(unsigned char dt, int len, int shots)
+{
+ for(int i=0; i<shots; i++) {
+ if(!DoBlkCmd(0x22,0x03,0xA2,len,&dt) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to get datatype %02X",dt);
+ return false;
+ }
+ if(buff[5]==0) return true;
+ if(!ParseDataType(dt&0x0F)) return false;
+ dt|=0x80; // get next item
+ }
+ return true;
+}
+
+bool cSmartCardNagra::ParseDataType(unsigned char dt)
+{
+ switch(dt) {
+ case IRDINFO:
+ {
+ irdProvId=(buff[10]*256)|buff[11];
+ char id[12];
+ memcpy(irdId,buff+17,4);
+ LDUMP(L_SC_INIT,irdId,4,"IRD system-ID: %04X, IRD ID:",irdProvId);
+ HexStr(id,irdId,sizeof(irdId));
+ infoStr.Printf("IRD system-ID: %04X\nIRD ID: %s\n",irdProvId,id);
+ return true;
+ }
+ case TIERS:
+ if(buff[7]==0x88 || buff[7]==0x08 || buff[7]==0x0C) {
+ int id=(buff[10]*256)|buff[11];
+ int tLowId=(buff[20]*256)|buff[21];
+ int tHiId=(buff[31]*256)|buff[32];
+ char date1[12], time1[12], date2[12], time2[12];
+ Date(buff+23,date1,time1);
+ Date(buff+27,date2,time2);
+
+ infoStr.Printf("|%04X|%04X |%04X |%s|\n",id,tLowId,tHiId,date1);
+ infoStr.Printf("| | | |%s|\n",date2);
+ PRINTF(L_SC_INIT,"|%04X|%04X|%04X|%s|%s|",id,tLowId,tHiId,date1,time1);
+ PRINTF(L_SC_INIT,"| | | |%s|%s|",date2,time2);
+ }
+ default:
+ return true;
+ }
+ return false;
+}
+
+bool cSmartCardNagra::DoCamExchange(const unsigned char *key)
+{
+ if(!GetCardStatus()) return false;
+ if(!DoBlkCmd(0x2A,0x02,0xAA,0x42) || !Status()) return false;
+ if(buff[4]!=64) {
+ PRINTF(L_SC_ERROR,"reading CAM 2A data failed");
+ return false;
+ }
+ unsigned char res[64];
+ if(!DecryptCamData(res,key,buff+5)) return false;
+ if(!DoBlkCmd(0x2B,0x42,0xAB,0x02,res) || !Status()) return false;
+ return true;
+}
+
+void cSmartCardNagra::Date(const unsigned char *data, char *dt, char *ti)
+{
+ int date=(data[0]<<8)|data[1];
+ int time=(data[2]<<8)|data[3];
+ struct tm t;
+ memset(&t,0,sizeof(struct tm));
+ t.tm_min =0;//-300;
+ t.tm_year=92;
+ t.tm_mday=date + 1;
+ t.tm_sec =(time - 1) * 2;
+ mktime(&t);
+ snprintf(dt,11,"%.2d.%.2d.%.4d",t.tm_mon+1,t.tm_mday,t.tm_year+1900);
+ snprintf(ti,9,"%.2d:%.2d:%.2d",t.tm_hour,t.tm_min,t.tm_sec);
+}
+
+bool cSmartCardNagra::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+ if(DoBlkCmd(data[3],data[4]+2,0x87,0x02,data+3+2) && Status()) {
+ cCondWait::SleepMs(100);
+ if(GetCardStatus() && HAS_CW &&
+ DoBlkCmd(0x1C,0x02,0x9C,0x36) && Status() &&
+ DecryptCW(cw,buff)) return true;
+ }
+ return false;
+}
+
+bool cSmartCardNagra::Update(int pid, int caid, const unsigned char *data)
+{
+ if(DoBlkCmd(data[8],data[9]+2,0x84,0x02,data+8+2) && Status()) {
+ cCondWait::SleepMs(500);
+ if(GetCardStatus()) return true;
+ }
+ return false;
+}
+
+bool cSmartCardNagra::DoBlkCmd(unsigned char cmd, int ilen, unsigned char res, int rlen, const unsigned char *data)
+{
+ unsigned char msg[MAX_LEN+3+1];
+
+ NAD(msg)=N2_NAD; PCB(msg)=0; PCB(msg)^=block; block^=0x40; LEN(msg)=ilen+6;
+ CLA(msg)=N2_CLA; INS(msg)=N2_INS; P1(msg)=N2_P1; P2(msg)=N2_P2; L(msg)=ilen;
+
+ int dlen=ilen-2;
+ if(dlen<0) {
+ PRINTF(L_SC_ERROR,"invalid data length encountered");
+ return false;
+ }
+
+ N2_CMD(msg)=cmd; N2_CLEN(msg)=dlen;
+
+ if(data && dlen>0) memcpy(&N2_DATA(msg),data,dlen);
+ int msglen=LEN(msg)+3;
+ msg[msglen-1]=rlen;
+ msg[msglen]=XorSum(msg,msglen);
+ msglen++;
+
+ if(SerWrite(msg,msglen)==msglen) {
+ LDUMP(L_CORE_SC,msg,msglen,"NAGRA: <-");
+ cCondWait::SleepMs(10);
+ if(SerRead(buff,3,cardCfg.workTO)!=3) {
+ PRINTF(L_SC_ERROR,"reading back reply failed");
+ return false;
+ }
+ int reslen=buff[2]+1;
+ if(SerRead(buff+3,reslen,cardCfg.workTO)!=reslen) {
+ PRINTF(L_SC_ERROR,"reading back information block failed");
+ return false;
+ }
+ if(XorSum(buff,reslen+3)) {
+ PRINTF(L_SC_ERROR,"checksum failed");
+ return false;
+ }
+ LDUMP(L_CORE_SC,buff,3+reslen,"NAGRA: ->");
+
+ if(buff[3]!=res) {
+ PRINTF(L_SC_ERROR,"result not expected (%02X != %02X)",buff[3],res);
+ return false;
+ }
+ memcpy(sb,&buff[3+rlen],2);
+ LDUMP(L_CORE_SC,sb,2,"NAGRA: ->");
+ cCondWait::SleepMs(10);
+ return true;
+ }
+ return false;
+}
+
+// -- cSmartCardLinkNagra -------------------------------------------------------------
+
+class cSmartCardLinkNagra : public cSmartCardLink {
+public:
+ cSmartCardLinkNagra(void):cSmartCardLink(SC_NAME,SC_ID) {}
+ virtual cSmartCard *Create(void) { return new cSmartCardNagra(); }
+ virtual cSmartCardData *CreateData(void) { return new cSmartCardDataNagra; }
+ };
+
+static cSmartCardLinkNagra staticScInit;
--- /dev/null
+#
+# Smartcard Nagra
+#
+TARGET = sc_nagra
+OBJS = sc-nagra.o
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "data.h"
+#include "misc.h"
+#include "opts.h"
+#include "parse.h"
+#include "log-sc.h"
+
+#define SYSTEM_SECA 0x0100
+
+#define SYSTEM_NAME "SC-Seca"
+#define SYSTEM_PRI -5
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_SECA)
+
+#define SC_NAME "Seca"
+#define SC_ID MAKE_SC_ID('S','e','c','a')
+
+#define SECADATE(buff,len,odate) { \
+ const unsigned char *dd=(odate); \
+ snprintf(buff,len,"%04d/%02d/%02d", \
+ ((dd[0]&0xFE)>>1)+1990, \
+ ((dd[0]&0x01)<<3) + ((dd[1]&0xE0)>>5), \
+ dd[1]&0x1F); \
+ }
+
+#define L_SC 12
+#define L_SC_PROC LCLASS(L_SC,L_SC_LASTDEF<<1)
+#define L_SC_ALL LALL(L_SC_PROC)
+
+static const struct LogModule lm_sc = {
+ (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+ "sc-seca",
+ { L_SC_DEFNAMES,"process" }
+ };
+ADD_MODULE(L_SC,lm_sc)
+
+static int blocker=0;
+static int ppv=false;
+
+// -- cSystemScSeca ---------------------------------------------------------------
+
+class cSystemScSeca : public cSystemScCore {
+public:
+ cSystemScSeca(void);
+ };
+
+cSystemScSeca::cSystemScSeca(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Seca")
+{
+ hasLogger=true;
+ needsDescrData=true;
+}
+
+// -- cSystemLinkScSeca --------------------------------------------------------
+
+static const tI18nPhrase Phrases[] = {
+ { "SC-Seca: EMM updates",
+ "SC-Seca: EMM updates",
+ "",
+ "",
+ "SC-Seca: EMM updates",
+ "",
+ "SC-Seca: Mise à jour EMM",
+ "",
+ "SC-Seca: EMM-päivitykset",
+ "SC-Seca: aktualizacje EMM",
+ "",
+ "",
+ "",
+ },
+ { "allow ALL",
+ "alle erlauben",
+ "",
+ "",
+ "ALLES toestaan",
+ "",
+ "Permettre tous",
+ "",
+ "salli kaikki",
+ "dopu¶æ wszystkie",
+ "",
+ "",
+ "",
+ },
+ { "block UNIQUE",
+ "UNIQUE blocken",
+ "",
+ "",
+ "UNIEKE blokkeren",
+ "",
+ "Bloquer UNIQUE",
+ "",
+ "estä uniikit",
+ "blokuj unikaty",
+ "",
+ "",
+ "",
+ },
+ { "block SHARED",
+ "SHARED blocken",
+ "",
+ "",
+ "GEDEELDE blokkeren",
+ "",
+ "Bloquer PARTAGE",
+ "",
+ "estä jaetut",
+ "blokuj dzielone",
+ "",
+ "",
+ "",
+ },
+ { "block ALL",
+ "alle blocken",
+ "",
+ "",
+ "ALLES blokkeren",
+ "",
+ "Bloquer TOUS",
+ "",
+ "estä kaikki",
+ "blokuj wszystkie",
+ "",
+ "",
+ "",
+ },
+ { "SC-Seca: activate PPV",
+ "SC-Seca: PPV aktivieren",
+ "",
+ "",
+ "SC-Seca: activeer PPV",
+ "",
+ "SC-Seca: activer PPV",
+ "",
+ "SC-Seca: Aktivoi PPV",
+ "SC-Seca: aktywuj PPV",
+ "",
+ "",
+ "",
+ },
+ { NULL }
+ };
+
+static const char *block[] = {
+ "allow ALL",
+ "block UNIQUE",
+ "block SHARED",
+ "block ALL"
+ };
+
+class cSystemLinkScSeca : public cSystemLink {
+public:
+ cSystemLinkScSeca(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemScSeca; }
+ };
+
+static cSystemLinkScSeca staticInit;
+
+cSystemLinkScSeca::cSystemLinkScSeca(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ opts=new cOpts(SYSTEM_NAME,2);
+ opts->Add(new cOptSel("Blocker","SC-Seca: EMM updates",&blocker,sizeof(block)/sizeof(char *),block));
+ cOpt *opt=new cOptBool("Ppv","SC-Seca: activate PPV",&ppv);
+ if(opt) opt->Persistant(false);
+ opts->Add(opt);
+ Feature.NeedsSmartCard();
+ Feature.AddPhrases(Phrases);
+}
+
+bool cSystemLinkScSeca::CanHandle(unsigned short SysId)
+{
+ SysId&=SYSTEM_MASK;
+ return smartcards.HaveCard(SC_ID) && SYSTEM_CAN_HANDLE(SysId);
+}
+
+// -- cProviderScSeca ----------------------------------------------------------
+
+class cProviderScSeca : public cProviderSeca {
+public:
+ int index;
+ unsigned char date[2], pbm[8];
+ char name[17];
+ //
+ cProviderScSeca(const unsigned char *pi, const unsigned char *s):cProviderSeca(pi,s) {}
+ };
+
+// -- cSmartCardSeca -----------------------------------------------------------
+
+struct SecaProvInfo {
+ unsigned char prov[2];
+ char name[16];
+ unsigned char sa[3];
+ unsigned char cb;
+ unsigned char date[2];
+ unsigned char rr;
+ unsigned char rstart;
+ unsigned char pbm[8];
+ unsigned char rend;
+ };
+
+struct SecaChannelInfo {
+ unsigned char pbm[8];
+ unsigned char date[2];
+ };
+
+class cSmartCardSeca : public cSmartCard, private cIdSet {
+private:
+ char datebuff[16];
+ //
+ const char *Date(const unsigned char *date);
+ bool CheckAccess(const unsigned char *data, const cProviderScSeca *p);
+public:
+ cSmartCardSeca(void);
+ virtual bool Init(void);
+ virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+ virtual bool Update(int pid, int caid, const unsigned char *data);
+ };
+
+static const struct StatusMsg msgs[] = {
+ // ECM status messages
+ { { 0x90,0x00 }, "Instruction executed without errors", true },
+ { { 0x90,0x02 }, "Signature failed", false },
+ { { 0x90,0x27 }, "Decoding the preview (non-error)", true },
+ { { 0x93,0x02 }, "No access, check your subscription", false },
+ { { 0x96,0x00 }, "Chain of nonvalid entrance or null event or all the nanos process, none decoding", false },
+ // EMM update status messages
+ { { 0x90,0x09 }, "Card update was not meant for this card", false },
+ { { 0x90,0x19 }, "Card update successfull, PPUA updated", true },
+ { { 0x97,0x00 }, "Card update was successful", true },
+ { { 0x97,0x40 }, "Card update was successful", true },
+ { { 0x97,0x50 }, "Card update was successful", true },
+ { { 0x97,0x78 }, "Card update was not necessary", false },
+ { { 0x97,0xe0 }, "EEPROM update was not necessary", false },
+ // unknown message
+ { { 0xFF,0xFF }, 0, false }
+ };
+
+static const struct CardConfig cardCfg = {
+ SM_8E2,2000,300
+ };
+
+cSmartCardSeca::cSmartCardSeca(void)
+:cSmartCard(&cardCfg,msgs)
+{}
+
+bool cSmartCardSeca::Init(void)
+{
+ static unsigned char ins0e[] = { 0xC1,0x0e,0x00,0x00,0x08 }; // get serial nr. (UA)
+ static unsigned char ins16[] = { 0xC1,0x16,0x00,0x00,0x07 }; // get nr. of providers
+ static unsigned char ins12[] = { 0xC1,0x12,0x00,0x00,0x19 }; // get provider info
+ static unsigned char ins34[] = { 0xC1,0x34,0x00,0x00,0x03 }; // request provider pbm (data=0x00 0x00 0x00)
+ static unsigned char ins32[] = { 0xC1,0x32,0x00,0x00,0x0A }; // get the pbm data (p1=provider)
+
+ static const unsigned char atrTester[] = { 0x0E,0x6C,0xB6,0xD6 };
+ if(atr->T!=0 || atr->histLen<7 || memcmp(&atr->hist[3],atrTester,4)) {
+ PRINTF(L_SC_INIT,"doesn't looks like a Seca card");
+ return false;
+ }
+
+ infoStr.Begin();
+ infoStr.Strcat("Seca smartcard\n");
+ char *type;
+ switch(atr->hist[0]*256+atr->hist[1]) {
+ case 0x5084: type="Generic"; break;
+ case 0x5384: type="Philips"; break;
+ case 0x5130:
+ case 0x5430:
+ case 0x5760: type="Thompson"; break;
+ case 0x5284:
+ case 0x5842:
+ case 0x6060: type="Siemens"; break;
+ case 0x7070: type="Canal+ NL"; break;
+ default: type="Unknown"; break;
+ }
+ snprintf(idStr,sizeof(idStr),"%s (%s %d.%d)",SC_NAME,type,atr->hist[2]&0x0F,atr->hist[2]>>4);
+ PRINTF(L_SC_INIT,"cardtype: %s %d.%d",type,atr->hist[2]&0x0F,atr->hist[2]>>4);
+
+ ResetIdSet();
+ unsigned char buff[MAX_LEN];
+ if(!IsoRead(ins0e,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"reading card serial failed");
+ return false;
+ }
+ SetCard(new cCardSeca(&buff[2]));
+ PRINTF(L_SC_INIT,"card serial number: %llu",Bin2LongLong(&buff[2],6));
+ infoStr.Printf("Type: %s %d.%d Serial: %llu\n",type,atr->hist[2]&0x0F,atr->hist[2]>>4,Bin2LongLong(&buff[2],6));
+
+ if(!IsoRead(ins16,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"reading provider map failed");
+ return false;
+ }
+ int provMap=buff[2]*256+buff[3];
+ if(LOG(L_SC_INIT)) {
+ int n=0, i=provMap;
+ do { n+=i&1; i>>=1; } while(i);
+ PRINTF(L_SC_INIT,"card has %d providers (0x%04x)",n,provMap);
+ }
+
+ for(int i=0 ; i<16 ; i++) {
+ if(provMap&(1<<i)) {
+ PRINTF(L_SC_INIT,"reading info for provider index %d",i);
+ ins12[2]=i;
+ if(!IsoRead(ins12,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"reading provider info failed");
+ return false;
+ }
+ ins32[2]=i;
+ static const unsigned char ins34data[] = { 0x00,0x00,0x00 };
+ if(!IsoWrite(ins34,ins34data) || !Status() ||
+ !IsoRead(ins32,&buff[ins12[4]]) || !Status())
+ memset(&buff[ins12[4]],0xFF,ins32[4]); // fake PBM response if card doesn't support command
+
+ struct SecaProvInfo *spi=(struct SecaProvInfo *)buff;
+ cProviderScSeca *p=new cProviderScSeca(spi->prov,spi->sa);
+ if(p) {
+ AddProv(p);
+ p->index=i;
+ strn0cpy(p->name,spi->name,sizeof(p->name));
+ memcpy(p->date,spi->date,sizeof(p->date));
+ memcpy(p->pbm,spi->pbm,sizeof(p->pbm));
+ }
+ infoStr.Printf("Prov %x (%.16s) until %s\n",p->provId[0]*256+p->provId[1],p->name,Date(p->date));
+ char str[20];
+ PRINTF(L_SC_INIT,"provider 0x%02x%02x '%.16s' expires %s pbm %s",
+ p->provId[0],p->provId[1],p->name,Date(p->date),HexStr(str,p->pbm,sizeof(p->pbm)));
+ }
+ }
+
+ infoStr.Finish();
+ return true;
+}
+
+const char *cSmartCardSeca::Date(const unsigned char *date)
+{
+ SECADATE(datebuff,sizeof(datebuff),date);
+ return datebuff;
+}
+
+bool cSmartCardSeca::CheckAccess(const unsigned char *data, const cProviderScSeca *p)
+{
+ struct SecaChannelInfo *sci=(struct SecaChannelInfo*)data;
+ if(sci->date[0]>p->date[0] || (sci->date[0]==p->date[0] && sci->date[1]>p->date[1]))
+ return false;
+ char str[20];
+ PRINTF(L_SC_PROC,"channelinfo date %s pbm %s",Date(sci->date),HexStr(str,sci->pbm,sizeof(sci->pbm)));
+ int result=0;
+ for(int i=7; i>=0; i--) // check pbm (is this right? seems to work though)
+ result|=(sci->pbm[i]&p->pbm[i]);
+ return (result!=0);
+}
+
+bool cSmartCardSeca::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+ static unsigned char ins3c[] = { 0xC1,0x3c,0x00,0x00,0x00 }; // coding cw
+ static unsigned char ins3a[] = { 0xC1,0x3a,0x00,0x00,0x10 }; // decoding cw
+ static unsigned char ins30[] = { 0xC1,0x30,0x00,0x02,0x09 }; // view ppv (active bx record)
+ static unsigned char ins30data[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFF };
+
+ cProviderScSeca *p=(cProviderScSeca *)FindProv(data);
+ if(p && ecm->Data()) {
+ PRINTF(L_SC_PROC,"provider 0x%04x index %d '%.16s' (expires %s)",cParseSeca::ProvId(data),p->index,p->name,Date(p->date));
+ if(CheckAccess(ecm->Data(),p)) {
+ if(ppv) {
+ PRINTF(L_SC_PROC,"activating PPV");
+ if(IsoWrite(ins30,ins30data)) Status();
+ ppv=false;
+ }
+ const unsigned char *payload;
+ ins3c[2]=p->index | (cParseSeca::SysMode(data) & 0xF0);
+ ins3c[3]=cParseSeca::KeyNr(data);
+ ins3c[4]=cParseSeca::Payload(data,&payload);
+ if(IsoWrite(ins3c,payload) && Status() &&
+ IsoRead(ins3a,cw) && Status()) return true;
+ }
+ else PRINTF(L_SC_ERROR,"update your subscription to view this channel");
+ }
+ return false;
+}
+
+bool cSmartCardSeca::Update(int pid, int caid, const unsigned char *data)
+{
+ static unsigned char ins40[] = { 0xC1,0x40,0x00,0x00,0x00 };
+
+ if(blocker==0 || (data[0]==0x82 && blocker==2) || (data[0]==0x84 && blocker==1)) {
+ cProviderScSeca *p=(cProviderScSeca *)FindProv(data);
+ if(p && MatchEMM(data)) {
+ PRINTF(L_SC_PROC,"got %s update",data[0]==0x82?"UNIQUE":"SHARED");
+ const unsigned char *payload;
+ ins40[2]=p->index | (cParseSeca::SysMode(data) & 0xF0);
+ ins40[3]=cParseSeca::KeyNr(data);
+ ins40[4]=cParseSeca::Payload(data,&payload);
+ if(IsoWrite(ins40,payload) && Status()) return Init();
+ }
+ }
+ return false;
+}
+
+// -- cSmartCardLinkSeca -------------------------------------------------------
+
+class cSmartCardLinkSeca : public cSmartCardLink {
+public:
+ cSmartCardLinkSeca(void):cSmartCardLink(SC_NAME,SC_ID) {}
+ virtual cSmartCard *Create(void) { return new cSmartCardSeca(); }
+ };
+
+static cSmartCardLinkSeca staticScInit;
--- /dev/null
+#
+# Smartcard Seca
+#
+TARGET = sc_seca
+OBJS = sc-seca.o
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "system-common.h"
+#include "smartcard.h"
+#include "parse.h"
+#include "misc.h"
+#include "log-sc.h"
+#include "log-core.h"
+
+#define SYSTEM_VIACCESS 0x0500
+
+#define SYSTEM_NAME "SC-Viaccess"
+#define SYSTEM_PRI -5
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_VIACCESS)
+
+#define SC_NAME "Viaccess"
+#define SC_ID MAKE_SC_ID('V','i','a','s')
+
+#define L_SC 13
+#define L_SC_ALL LALL(L_SC_LASTDEF)
+
+static const struct LogModule lm_sc = {
+ (LMOD_ENABLE|L_SC_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SC_DEFDEF)&LOPT_MASK,
+ "sc-viaccess",
+ { L_SC_DEFNAMES }
+ };
+ADD_MODULE(L_SC,lm_sc)
+
+// -- cSystemScViaccess ------------------------------------------------------------------
+
+class cSystemScViaccess : public cSystemScCore { //, private cTPS {
+public:
+ cSystemScViaccess(void);
+ virtual void ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, const unsigned char *data, int len);
+ };
+
+cSystemScViaccess::cSystemScViaccess(void)
+:cSystemScCore(SYSTEM_NAME,SYSTEM_PRI,SC_ID,"SC Viaccess")
+{
+ hasLogger=true;
+}
+
+void cSystemScViaccess::ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, const unsigned char *data, int len)
+{
+ const int pid=WORD(data,2,0x1FFF);
+ if(pid>=0xAA && pid<=0xCF) {
+ PRINTF(L_CORE_ECMPROC,"sc-viaccess: dropped \"fake\" ecm pid 0x%04xn",pid);
+ return;
+ }
+ cSystem::ParseCADescriptor(ecms,sysId,data,len);
+}
+
+// -- cSystemLinkScViaccess --------------------------------------------------------------
+
+class cSystemLinkScViaccess : public cSystemLink {
+public:
+ cSystemLinkScViaccess(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemScViaccess; }
+ };
+
+static cSystemLinkScViaccess staticInit;
+
+cSystemLinkScViaccess::cSystemLinkScViaccess(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsSmartCard();
+}
+
+bool cSystemLinkScViaccess::CanHandle(unsigned short SysId)
+{
+ return smartcards.HaveCard(SC_ID) && SYSTEM_CAN_HANDLE(SysId);
+}
+
+// -- cProviderScViaccess ----------------------------------------------------------
+
+class cProviderScViaccess : public cProviderViaccess {
+public:
+ unsigned char availKeys[16];
+ char name[33];
+ //
+ cProviderScViaccess(const unsigned char *id, const unsigned char *s):cProviderViaccess(id,s) {}
+ };
+
+// -- cSmartCardViaccess -----------------------------------------------------------------
+
+class cSmartCardViaccess : public cSmartCard, public cIdSet {
+private:
+ unsigned char lastId[3];
+ //
+ unsigned char GetSW1(void) { return sb[1]; }
+ bool CheckKey(cProviderScViaccess *p, const unsigned char keynr);
+ bool SetProvider(const unsigned char *id);
+public:
+ cSmartCardViaccess(void);
+ virtual bool Init(void);
+ virtual bool Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw);
+ virtual bool Update(int pid, int caid, const unsigned char *data);
+ };
+
+static const struct StatusMsg msgs[] = {
+ { { 0x6b,0x00 }, "Instruction not supported", false },
+ { { 0x6d,0x00 }, "Instruction not supported", false },
+ { { 0x90,0x00 }, "Instruction executed without errors", true },
+ { { 0x90,0x08 }, "Instruction executed without errors", true },
+ { { 0xFF,0xFF }, 0, false }
+ };
+
+static const struct CardConfig cardCfg = {
+ SM_8O2,500,300
+ };
+
+cSmartCardViaccess::cSmartCardViaccess(void)
+:cSmartCard(&cardCfg,msgs)
+{
+ memset(lastId,0,sizeof(lastId));
+}
+
+bool cSmartCardViaccess::Init(void)
+{
+ static const unsigned char verifyBytes[] = { 0x90,0x00 };
+ if(atr->T!=0 || (atr->histLen<7 && memcmp(atr->hist+(atr->histLen-2),verifyBytes,sizeof(verifyBytes)))) {
+ PRINTF(L_SC_INIT,"doesn't look like a Viaccess card");
+ return false;
+ }
+
+ infoStr.Begin();
+ infoStr.Strcat("Viaccess smartcard\n");
+ char *ver=0;
+ switch((atr->hist[atr->histLen-4]<<8)|atr->hist[atr->histLen-3]) {
+ case 0x6268: ver="2.3"; break;
+ case 0x6668: ver="2.4(?)"; break;
+ case 0xa268:
+ default: ver="unknown"; break;
+ }
+
+ PRINTF(L_SC_INIT,"card v.%s",ver);
+ snprintf(idStr,sizeof(idStr),"%s (V.%s)",SC_NAME,ver);
+
+ static unsigned char insac[] = { 0xca, 0xac, 0x00, 0x00, 0x00 }; // select data
+ static unsigned char insb8[] = { 0xca, 0xb8, 0x00, 0x00, 0x00 }; // read selected data
+ static unsigned char insa4[] = { 0xca, 0xa4, 0x00, 0x00, 0x00 }; // select issuer
+ static unsigned char insc0[] = { 0xca, 0xc0, 0x00, 0x00, 0x00 }; // read data item
+ unsigned char buff[MAX_LEN];
+
+ ResetIdSet();
+ insac[2]=0xa4; // request unique id
+ if(!IsoWrite(insac,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to request ua");
+ return false;
+ }
+ insb8[4]=0x07; // read unique id
+ if(!IsoRead(insb8,buff) || !Status() || buff[1]!=0x05) {
+ PRINTF(L_SC_ERROR,"failed to read ua");
+ return false;
+ }
+ SetCard(new cCardViaccess(&buff[2]));
+ PRINTF(L_SC_INIT,"card UA: %llu",Bin2LongLong(&buff[2],5));
+ infoStr.Printf("Card v.%s UA %010llu\n",ver,Bin2LongLong(&buff[2],5));
+
+ insa4[2]=0x00; // select issuer 0
+ if(!IsoWrite(insa4,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to select issuer 0");
+ return false;
+ }
+ do {
+ insc0[4]=0x1a; // show provider properties
+ if(!IsoRead(insc0,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to read prov properties");
+ return false;
+ }
+
+ unsigned char buff2[MAX_LEN];
+ insac[2]=0xa5; // request sa
+ if(!IsoWrite(insac,buff2) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to request sa");
+ return false;
+ }
+ insb8[4]=0x06; // read sa
+ if(!IsoRead(insb8,buff2) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to read sa");
+ return false;
+ }
+ cProviderScViaccess *p=new cProviderScViaccess(&buff[0],&buff2[2]);
+ if(p) {
+ AddProv(p);
+ memcpy(p->availKeys,buff+10,sizeof(p->availKeys));
+
+ insac[2]=0xa7; // request name
+ if(!IsoWrite(insac,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to request prov name");
+ return false;
+ }
+ insb8[4]=0x02; // read name nano + len
+ if(!IsoRead(insb8,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to read prov name length");
+ return false;
+ }
+ unsigned int nameLen=buff[1];
+ if(nameLen>=sizeof(p->name)) {
+ PRINTF(L_SC_ERROR,"provider name buffer overflow");
+ nameLen=sizeof(p->name)-1;
+ }
+ insb8[4]=nameLen;
+ if(!IsoRead(insb8,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to read prov name");
+ return false;
+ }
+ memcpy(p->name,buff,nameLen); p->name[nameLen]=0;
+
+ PRINTF(L_SC_INIT,"provider %06x (%s)",(int)p->ProvId(),p->name);
+ infoStr.Printf("Prov %06x (%s) SA %08u\n",(int)p->ProvId(),p->name,Bin2Int(&buff2[2],4));
+ }
+ else PRINTF(L_SC_ERROR,"no memory for provider");
+
+ insa4[2]=0x02; // next issuer
+ if(!IsoWrite(insa4,buff) || !Status()) {
+ PRINTF(L_SC_ERROR,"failed to select next issuer");
+ return false;
+ }
+ } while(GetSW1()==0x00);
+ infoStr.Finish();
+ return true;
+}
+
+bool cSmartCardViaccess::CheckKey(cProviderScViaccess *p, const unsigned char keynr)
+{
+ for(unsigned int j=0; j<sizeof(p->availKeys); j++)
+ if(p->availKeys[j]==keynr) return true;
+ return false;
+}
+
+bool cSmartCardViaccess::SetProvider(const unsigned char *id)
+{
+ static const unsigned char insa4[] = { 0xca,0xa4,0x04,0x00,0x03 };
+
+ if(id[0]!=lastId[0] || id[1]!=lastId[1] || (id[2]&0xF0)!=lastId[2]) {
+ memcpy(lastId,id,3);
+ lastId[2]&=0xF0;
+ if(!IsoWrite(insa4,lastId) || !Status()) return false;
+ }
+ return true;
+}
+
+bool cSmartCardViaccess::Decode(const cEcmInfo *ecm, const unsigned char *data, unsigned char *cw)
+{
+ static unsigned char ins88[] = { 0xca,0x88,0x00,0x00,0x00 }; // set ecm
+ static unsigned char insf8[] = { 0xca,0xf8,0x00,0x00,0x00 }; // set geographic info
+ static unsigned char insc0[] = { 0xca,0xc0,0x00,0x00,0x12 }; // read dcw
+
+ cProviderScViaccess *p=(cProviderScViaccess *)FindProv(data);
+ if(p) {
+ unsigned char keynr=cParseViaccess::KeyNr(data);
+ if(CheckKey(p,keynr)) {
+ if(!SetProvider(cParseViaccess::ProvIdPtr(data))) return false;
+
+ const unsigned char *start=cParseViaccess::NanoStart(data)+5;
+ const unsigned char *ecm88Data=start;
+ int ecm88Len=SCT_LEN(data)-(start-data);
+ int ecmf8Len=0;
+ while(ecm88Len>0 && ecm88Data[0]<0xA0) {
+ const int nanoLen=ecm88Data[1]+2;
+ ecmf8Len+=nanoLen;
+ ecm88Len-=nanoLen; ecm88Data+=nanoLen;
+ }
+ if(ecmf8Len) {
+ insf8[3]=keynr;
+ insf8[4]=ecmf8Len;
+ if(!IsoWrite(insf8,start) || !Status()) return false;
+ }
+
+ ins88[2]=ecmf8Len?1:0;
+ ins88[3]=keynr;
+ ins88[4]=ecm88Len;
+ if(!IsoWrite(ins88,ecm88Data) || !Status()) return false; // request dcw
+ unsigned char buff[MAX_LEN];
+ if(!IsoRead(insc0,buff) || !Status()) return false; // read dcw
+ switch(buff[0]) {
+ case 0xe8: // even
+ if(buff[1]==8) { memcpy(cw,buff+2,8); return true; }
+ break;
+ case 0xe9: // odd
+ if(buff[1]==8) { memcpy(cw+8,buff+2,8); return true; }
+ break;
+ case 0xea: // complete
+ if(buff[1]==16) { memcpy(cw,buff+2,16); return true; }
+ break;
+ }
+ }
+ else PRINTF(L_SC_ERROR,"key not found on card");
+ }
+ else PRINTF(L_SC_ERROR,"provider not found on card");
+ return false;
+}
+
+bool cSmartCardViaccess::Update(int pid, int caid, const unsigned char *data)
+{
+ static unsigned char insf0[] = { 0xca,0xf0,0x00,0x00,0x00 };
+ static unsigned char ins18[] = { 0xca,0x18,0x00,0x00,0x00 };
+
+ int updtype;
+ cAssembleData ad(data);
+ if(MatchAndAssemble(&ad,&updtype,0)) {
+ while((data=ad.Assembled())) {
+ const unsigned char *start=cParseViaccess::NanoStart(data);
+ int nanolen=SCT_LEN(data)-(start-data);
+
+ if(start && start[0]==0x90 && start[1]==0x03 && nanolen>=5 && SetProvider(&start[2])) {
+ insf0[3]=ins18[3]=cParseViaccess::KeyNrFromNano(start);
+ start+=5; nanolen-=5;
+
+ int n;
+ if(start[0]==0x9e && nanolen>=(n=start[1]+2)) {
+ insf0[4]=n;
+ if(!IsoWrite(insf0,start) || !Status()) continue;
+ start+=n; nanolen-=n;
+ }
+
+ if(nanolen>0) {
+ ins18[2]=updtype>0 ? updtype-1 : updtype;
+ ins18[4]=nanolen;
+ if(IsoWrite(ins18,start)) Status();
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+// -- cSmartCardLinkViaccess -------------------------------------------------------------
+
+class cSmartCardLinkViaccess : public cSmartCardLink {
+public:
+ cSmartCardLinkViaccess(void):cSmartCardLink(SC_NAME,SC_ID) {}
+ virtual cSmartCard *Create(void) { return new cSmartCardViaccess(); }
+ };
+
+static cSmartCardLinkViaccess staticScInit;
--- /dev/null
+#
+# Smartcard Viaccess
+#
+TARGET = sc_viaccess
+OBJS = sc-viaccess.o
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <byteswap.h>
+
+#include <openssl/sha.h>
+
+#include "system-common.h"
+#include "data.h"
+#include "helper.h"
+#include "crypto.h"
+#include "parse.h"
+#include "misc.h"
+#include "log-sys.h"
+#include "log-core.h"
+
+#define SYSTEM_SECA 0x0100
+
+#define SYSTEM_NAME "Seca"
+#define SYSTEM_PRI -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_SECA)
+
+#define FILEMAP_DOMAIN "seca"
+
+#define L_SYS 14
+#define L_SYS_ALL LALL(L_SYS_LASTDEF)
+
+static const struct LogModule lm_sys = {
+ (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+ "seca",
+ { L_SYS_DEFNAMES }
+ };
+ADD_MODULE(L_SYS,lm_sys)
+
+// -- cPlainKeySeca ------------------------------------------------------------
+
+#define PLAINLEN_SECA_H 8
+#define PLAINLEN_SECA_D 16
+#define PLAINLEN_SECA_B 90
+#define PLAINLEN_SECA_E 6 // exponent key len
+#define EMM_MAGIC 1
+#define N51_MAGIC 2
+
+class cPlainKeySeca : public cDualKey {
+protected:
+ virtual bool IsBNKey(void) const;
+ virtual int IdSize(void) { return 4; }
+ virtual cString PrintKeyNr(void);
+public:
+ cPlainKeySeca(bool Super);
+ virtual bool Parse(const char *line);
+ };
+
+static cPlainKeyTypeReg<cPlainKeySeca,'S'> KeyReg;
+
+cPlainKeySeca::cPlainKeySeca(bool Super)
+:cDualKey(Super,true)
+{}
+
+bool cPlainKeySeca::IsBNKey(void) const
+{
+ switch(keynr&C2MASK) {
+ case MBC('E','1'):
+ case MBC('M','1'):
+ case MBC('E','3'):
+ case MBC('M','3'):
+ case MBC('E','5'):
+ case MBC('M','5'):
+ case MBC('E','9'):
+ case MBC('M','9'): return true;
+ }
+ return false;
+}
+
+bool cPlainKeySeca::Parse(const char *line)
+{
+ unsigned char sid[2];
+ const char *sline=line;
+ int len;
+ if(GetChar(line,&type,1) && (len=GetHex(line,sid,2,false))) {
+ type=toupper(type); id=Bin2Int(sid,len);
+ line=skipspace(line);
+ int emmnr=0, keylen=PLAINLEN_SECA_B;
+ if(!strncasecmp(line,"EMM",3)) { // EMM RSA key
+ emmnr=EMM_MAGIC;
+ line+=3;
+ line=skipspace(line);
+ }
+ else if(!strncasecmp(line,"N51",3)) { // Nano 51 RSA key
+ emmnr=N51_MAGIC;
+ line+=3;
+ line=skipspace(line);
+ keylen=toupper(*line)=='E'?1:129;
+ }
+ bool ok;
+ switch(toupper(*line)) {
+ case 'E':
+ case 'M': ok=GetChar(line,&keynr,2);
+ keynr=ADDC3(keynr,emmnr);
+ break;
+ default: ok=emmnr ? false : GetHex(line,sid,1);
+ keynr=sid[0];
+ break;
+ }
+ if(ok) {
+ unsigned char skey[keylen];
+ len=GetHex(line,skey,keylen,false);
+ if(IsBNKey()) {
+ if(C2(keynr)=='E' && len==PLAINLEN_SECA_E) {
+ // support short exponent keys
+ memset(&skey[len],0,keylen-len);
+ len=keylen;
+ }
+ }
+ else {
+ if(len==PLAINLEN_SECA_H || len==PLAINLEN_SECA_D) {
+ // support for 16 & 8 byte keys
+ keylen=len;
+ }
+ }
+ if(len==keylen) {
+ SetBinKey(skey,keylen);
+ return true;
+ }
+ }
+ }
+ FormatError("seca",sline);
+ return false;
+}
+
+cString cPlainKeySeca::PrintKeyNr(void)
+{
+ char nr[32];
+ int q=0;
+ switch(C3(keynr)) {
+ case EMM_MAGIC: q+=snprintf(nr+q,sizeof(nr)-q,"EMM "); break;
+ case N51_MAGIC: q+=snprintf(nr+q,sizeof(nr)-q,"N51 "); break;
+ }
+ if(IsBNKey()) {
+ nr[q ]=(keynr>>8) & 0xff;
+ nr[q+1]= keynr & 0xff;
+ nr[q+2]=0;
+ q+=2;
+ }
+ else q+=snprintf(nr+q,sizeof(nr)-q,"%.2X",keynr);
+ return nr;
+}
+
+// -- cSecaCardInfo ------------------------------------------------------------
+
+class cSecaCardInfo : public cProviderSeca {
+private:
+ int len;
+public:
+ unsigned char key[16];
+ //
+ bool Parse(const char *line);
+ bool Save(FILE *f) { return true; }
+ bool IsUpdated(void) { return false; }
+ void Updated(void) {}
+ bool Cmp(cSecaCardInfo *ci) { return false; }
+ int KeySize(void) { return len; }
+ };
+
+bool cSecaCardInfo::Parse(const char *line)
+{
+ return GetHex(line,provId,sizeof(provId)) &&
+ GetHex(line,sa,sizeof(sa)) &&
+ (len=GetHex(line,key,sizeof(key),false)) &&
+ (len==PLAINLEN_SECA_H || len==PLAINLEN_SECA_D);
+}
+
+// -- cSecaCardInfos -----------------------------------------------------------
+
+class cSecaCardInfos : public cCardInfos<cSecaCardInfo> {
+public:
+ cSecaCardInfos(void):cCardInfos<cSecaCardInfo>(SYSTEM_NAME) {}
+ };
+
+static cSecaCardInfos Scards;
+
+// -- cSeca --------------------------------------------------------------------
+
+static const unsigned char secaPC1[] = {
+ 42,57,29,34, 41,53,30,15,
+ 19,36,23,14, 43,61,12, 3,
+ 51,49,5, 6, 45,54,52,47,
+ 63,38,58,22, 60,33,10,26,
+ 37,35,44, 1, 20,62,28,18,
+ 46, 9,39, 4, 27,11,21,50,
+ 31,25, 2, 7, 13,55,59,17
+ };
+
+static const unsigned char secaPC2[] = {
+ 18, 3,21,15, 42,35,37, 8,
+ 49,41,30,55, 56,29,12,23,
+ 43,14,7 ,27, 13, 2,11,45,
+ 4,34,54,51, 22,40,16,25,
+ 26,48,53,28, 1,17, 5,31,
+ 50, 6,39,24, 33,47,38,32
+ };
+
+static inline void __swap8_4(unsigned char *data)
+{
+ unsigned char temp[4];
+ memcpy(temp,data,4);
+ memcpy(data,&data[4],4);
+ memcpy(&data[4],temp,4);
+}
+#define swap8_4(d) __swap8_4(d)
+
+class cSeca {
+private:
+ static const unsigned char TD[];
+ //
+ void Fase(unsigned char *data, const unsigned char *key, const unsigned char *T1, const unsigned char *T2);
+protected:
+ static const unsigned char T1_S1[], T2_S1[];
+ //
+ void Decrypt(unsigned char *data, const unsigned char *key, const unsigned char *T1, const unsigned char *T2);
+ void Encrypt(unsigned char *data, const unsigned char *key, const unsigned char *T1, const unsigned char *T2);
+ void CalcSignature(const unsigned char *buff, int len, unsigned char *signature, const unsigned char *k, const unsigned char *T1, const unsigned char *T2);
+ // Seca2 functions
+ void AdditionalAlgo(unsigned char *data, const unsigned char *key, const int mode);
+ };
+
+const unsigned char cSeca::TD[4] = { 1,3,0,2 };
+
+const unsigned char cSeca::T1_S1[256] = { // Original Tables
+ 0x2a,0xe1,0x0b,0x13,0x3e,0x6e,0x32,0x48, 0xd3,0x31,0x08,0x8c,0x8f,0x95,0xbd,0xd0,
+ 0xe4,0x6d,0x50,0x81,0x20,0x30,0xbb,0x75, 0xf5,0xd4,0x7c,0x87,0x2c,0x4e,0xe8,0xf4,
+ 0xbe,0x24,0x9e,0x4d,0x80,0x37,0xd2,0x5f, 0xdb,0x04,0x7a,0x3f,0x14,0x72,0x67,0x2d,
+ 0xcd,0x15,0xa6,0x4c,0x2e,0x3b,0x0c,0x41, 0x62,0xfa,0xee,0x83,0x1e,0xa2,0x01,0x0e,//8
+ 0x7f,0x59,0xc9,0xb9,0xc4,0x9d,0x9b,0x1b, 0x9c,0xca,0xaf,0x3c,0x73,0x1a,0x65,0xb1,
+ 0x76,0x84,0x39,0x98,0xe9,0x53,0x94,0xba, 0x1d,0x29,0xcf,0xb4,0x0d,0x05,0x7d,0xd1,
+ 0xd7,0x0a,0xa0,0x5c,0x91,0x71,0x92,0x88, 0xab,0x93,0x11,0x8a,0xd6,0x5a,0x77,0xb5,
+ 0xc3,0x19,0xc1,0xc7,0x8e,0xf9,0xec,0x35, 0x4b,0xcc,0xd9,0x4a,0x18,0x23,0x9f,0x52,//16
+ 0xdd,0xe3,0xad,0x7b,0x47,0x97,0x60,0x10, 0x43,0xef,0x07,0xa5,0x49,0xc6,0xb3,0x55,
+ 0x28,0x51,0x5d,0x64,0x66,0xfc,0x44,0x42, 0xbc,0x26,0x09,0x74,0x6f,0xf7,0x6b,0x4f,
+ 0x2f,0xf0,0xea,0xb8,0xae,0xf3,0x63,0x6a, 0x56,0xb2,0x02,0xd8,0x34,0xa4,0x00,0xe6,
+ 0x58,0xeb,0xa3,0x82,0x85,0x45,0xe0,0x89, 0x7e,0xfd,0xf2,0x3a,0x36,0x57,0xff,0x06,//24
+ 0x69,0x54,0x79,0x9a,0xb6,0x6c,0xdc,0x8b, 0xa7,0x1f,0x90,0x03,0x17,0x1c,0xed,0xd5,
+ 0xaa,0x5e,0xfe,0xda,0x78,0xb0,0xbf,0x12, 0xa8,0x22,0x21,0x3d,0xc2,0xc0,0xb7,0xa9,
+ 0xe7,0x33,0xfb,0xf1,0x70,0xe5,0x17,0x96, 0xf8,0x8d,0x46,0xa1,0x86,0xe2,0x40,0x38,
+ 0xf6,0x68,0x25,0x16,0xac,0x61,0x27,0xcb, 0x5b,0xc8,0x2b,0x0f,0x99,0xde,0xce,0xc5
+ };
+
+const unsigned char cSeca::T2_S1[256] = {
+ 0xbf,0x11,0x6d,0xfa,0x26,0x7f,0xf3,0xc8, 0x9e,0xdd,0x3f,0x16,0x97,0xbd,0x08,0x80,
+ 0x51,0x42,0x93,0x49,0x5b,0x64,0x9b,0x25, 0xf5,0x0f,0x24,0x34,0x44,0xb8,0xee,0x2e,
+ 0xda,0x8f,0x31,0xcc,0xc0,0x5e,0x8a,0x61, 0xa1,0x63,0xc7,0xb2,0x58,0x09,0x4d,0x46,
+ 0x81,0x82,0x68,0x4b,0xf6,0xbc,0x9d,0x03, 0xac,0x91,0xe8,0x3d,0x94,0x37,0xa0,0xbb, //8
+ 0xce,0xeb,0x98,0xd8,0x38,0x56,0xe9,0x6b, 0x28,0xfd,0x84,0xc6,0xcd,0x5f,0x6e,0xb6,
+ 0x32,0xf7,0x0e,0xf1,0xf8,0x54,0xc1,0x53, 0xf0,0xa7,0x95,0x7b,0x19,0x21,0x23,0x7d,
+ 0xe1,0xa9,0x75,0x3e,0xd6,0xed,0x8e,0x6f, 0xdb,0xb7,0x07,0x41,0x05,0x77,0xb4,0x2d,
+ 0x45,0xdf,0x29,0x22,0x43,0x89,0x83,0xfc, 0xd5,0xa4,0x88,0xd1,0xf4,0x55,0x4f,0x78,//16
+ 0x62,0x1e,0x1d,0xb9,0xe0,0x2f,0x01,0x13, 0x15,0xe6,0x17,0x6a,0x8d,0x0c,0x96,0x7e,
+ 0x86,0x27,0xa6,0x0d,0xb5,0x73,0x71,0xaa, 0x36,0xd0,0x06,0x66,0xdc,0xb1,0x2a,0x5a,
+ 0x72,0xbe,0x3a,0xc5,0x40,0x65,0x1b,0x02, 0x10,0x9f,0x3b,0xf9,0x2b,0x18,0x5c,0xd7,
+ 0x12,0x47,0xef,0x1a,0x87,0xd2,0xc2,0x8b, 0x99,0x9c,0xd3,0x57,0xe4,0x76,0x67,0xca,//24
+ 0x3c,0xfb,0x90,0x20,0x14,0x48,0xc9,0x60, 0xb0,0x70,0x4e,0xa2,0xad,0x35,0xea,0xc4,
+ 0x74,0xcb,0x39,0xde,0xe7,0xd4,0xa3,0xa5, 0x04,0x92,0x8c,0xd9,0x7c,0x1c,0x7a,0xa8,
+ 0x52,0x79,0xf2,0x33,0xba,0x1f,0x30,0x9a, 0x00,0x50,0x4c,0xff,0xe5,0xcf,0x59,0xc3,
+ 0xe3,0x0a,0x85,0xb3,0xae,0xec,0x0b,0xfe, 0xe2,0xab,0x4a,0xaf,0x69,0x6c,0x2c,0x5d
+ };
+
+void cSeca::Fase(unsigned char *D, const unsigned char *key, const unsigned char *T1, const unsigned char *T2)
+{
+ for(int l=0; l<4; ++l) { D[l]^=key[l]; D[l]=T1[D[l]&0xff]; }
+ for(int l=6; l>3; --l) {
+ D[(l+2)&3]^=D[(l+1)&3];
+ D[l&3]=T2[(sn8(D[(l+1)&3])+D[l&3])&0xff];
+ }
+ for(int l=3; l>0; --l) {
+ D[(l+2)&3]^=D[(l+1)&3];
+ D[l&3]=T1[(sn8(D[(l+1)&3])+D[l&3])&0xff];
+ }
+ D[2]^=D[1];
+ D[1]^=D[0];
+}
+
+void cSeca::Decrypt(unsigned char *d, const unsigned char *key, const unsigned char *T1, const unsigned char *T2)
+{
+ unsigned char k[16],D[4];
+ memcpy(k,key,sizeof(k));
+ unsigned char C=0xff;
+ for(int j=0; j<4; ++j) {
+ for(int i=0; i<16; ++i) {
+ if((i&3)==0) ++C;
+ k[i]^=T1[(k[(15+i)&0xf]^k[(i+1)&0xf]^C)&0xff];
+ }
+ }
+ int j=0;
+ for(int i=0; i<16; ++i) {
+ for(int l=0; l<4; ++l) D[l]=d[l+4];
+ j=(j+12)&0xf;
+ Fase(D,&k[j&0xc],T1,T2);
+ for(int l=0; l<4; ++l) d[l]^=T2[D[TD[l]]&0xff];
+ for(int l=3; l>=0; --l) k[(j+l)&0xf]^=T1[(k[(j+l+1)&0xf]^k[(j+l+15)&0xf]^(15-i))&0xff];
+ if(i<15) swap8_4(d);
+ }
+}
+
+void cSeca::Encrypt(unsigned char *d, const unsigned char *key, const unsigned char *T1, const unsigned char *T2)
+{
+ unsigned char D[4],kk[16];
+ memcpy(kk, key, sizeof(kk));
+ for(int j=0, i=0; i<16; ++i, j=(j+4)&0xf) {
+ for(int l=0; l<4; ++l) kk[(j+l)&0xf] ^= T1[(kk[(j+l+1)&0xf]^kk[(j+l+15)&0xf]^i)&0xff];
+ if(i>0) swap8_4(d);
+ for(int l=0; l<4; ++l) D[l]=d[l+4];
+ Fase(D,&kk[j&0xc],T1,T2);
+ for(int l=0; l<4; ++l) d[l]^=T2[D[TD[l]]&0xff];
+ }
+}
+
+void cSeca::CalcSignature(const unsigned char *buff, int len, unsigned char *signature, const unsigned char *k, const unsigned char *T1, const unsigned char *T2)
+{
+ memset(signature,0,8);
+ for(int i=0 ; i<len ; i+=8) {
+ for(int j=0 ; j<8 && i+j<len; j++) signature[j]^=buff[i+j];
+ Encrypt(signature,k,T1,T2);
+ }
+}
+
+void cSeca::AdditionalAlgo(unsigned char *data, const unsigned char *key, const int mode)
+{
+ static const unsigned int adders[] = {
+ 0x0000,0xe555,0xafff,0x5ffe,0xf552,0x6ffb,0xcff9,0x154c,
+ 0x3ff4,0x4ff1,0x4543,0x1fea,0xdfe6,0x8537,0x0fdd,0x7fd8
+ };
+ const unsigned short * const k = (const unsigned short *)key;
+ unsigned short * const dd = (unsigned short *)data;
+ unsigned int d1=dd[0], d2=dd[1], d3=dd[2], d4=dd[3];
+ PRINTF(L_SYS_VERBOSE,"additional algo: %s",!mode?"encrypt":"decrypt");
+ if(!mode) {
+ for(int i=0; i<0x10; i++) {
+ const unsigned int adder = adders[i];
+ d1 += (k[0] + d3 + adder) ^ (k[1] + d4 + adder);
+ d2 += (k[2] + d3 + adder) ^ (k[3] + d4 + adder);
+ d1 = ror16(d1,5);
+ d2 = rol16(d2,3);
+ d3 += (k[0] + d1 + adder) ^ (k[1] + d2 + adder);
+ d4 += (k[2] + d1 + adder) ^ (k[3] + d2 + adder);
+ d3 = rol16(d3,3);
+ d4 = ror16(d4,5);
+ }
+ }
+ else {
+ for(int i=0xf; i>=0; i--) {
+ const unsigned int adder = adders[i];
+ d4 = rol16(d4,5);
+ d3 = ror16(d3,3);
+ d4 -= (k[2] + d1 + adder) ^ (k[3] + d2 + adder);
+ d3 -= (k[0] + d1 + adder) ^ (k[1] + d2 + adder);
+ d2 = ror16(d2,3);
+ d1 = rol16(d1,5);
+ d2 -= (k[2] + d3 + adder) ^ (k[3] + d4 + adder);
+ d1 -= (k[0] + d3 + adder) ^ (k[1] + d4 + adder);
+ }
+ }
+ dd[0]=d1; dd[1]=d2; dd[2]=d3; dd[3]=d4;
+}
+
+// -- cSeca2Prov ---------------------------------------------------------------
+
+#define MAX_PERMS 10
+#define KEY2INDEX(x) (((x)>>5)&0x03)
+
+struct Perm {
+ unsigned char P1[8], P2[8], P3[8], P4[8];
+ };
+
+struct ProvData {
+ unsigned short LoadId, MTLoadSize, MTSize;
+ unsigned char SHA1Pad, SHA1End;
+ unsigned char FP[8];
+ };
+
+class cSeca2Prov: protected cSeca {
+private:
+ cFileMap *hash, *mask;
+ unsigned short id;
+ //
+ unsigned short Sum(const unsigned char *data, int n) const;
+protected:
+ const struct ProvData *pData;
+ const struct Perm *perm;
+ //
+ cFileMap *GetMap(const char *type, int size, bool generic) const;
+public:
+ cSeca2Prov(unsigned short Id);
+ virtual ~cSeca2Prov();
+ // provider specific mods
+ virtual void PreSSE(unsigned char *data, int pos) {}
+ virtual void PostSSE(unsigned char *data, int pos) {}
+ virtual void SignatureMod(unsigned char *MD, const unsigned char *PK) {}
+ virtual void PreCW(unsigned char *data) {}
+ virtual void PostCW(unsigned char *data) {}
+ virtual void ChainTableXor(unsigned char *data, unsigned short index)=0;
+ virtual bool DoSigCheck(void) { return true; }
+ //
+ virtual bool Init(void);
+ void CalcSHASignature(const unsigned char *data, int n, unsigned char *signature, bool PadMode=true);
+ bool DecryptSE(unsigned char *data, const unsigned char *key, int n, int start, const unsigned char *T1, const unsigned char *T2);
+ bool Matches(unsigned short Id) const { return Id==id; }
+ //
+ const unsigned char *T1(int index) const;
+ const unsigned char *T2(int index) const;
+ const struct Perm *P(int index) const;
+ inline const unsigned char *MT(void) const { return mask->Addr(); }
+ inline int MTSize(void) const { return pData->MTSize; }
+ inline const unsigned char *FP(void) const { return pData->FP; }
+ };
+
+cSeca2Prov::cSeca2Prov(unsigned short Id)
+{
+ id=Id;
+ pData=0; perm=0; hash=0; mask=0;
+}
+
+cSeca2Prov::~cSeca2Prov()
+{
+ if(hash) hash->Unmap();
+ if(mask) mask->Unmap();
+}
+
+bool cSeca2Prov::Init(void)
+{
+ hash=GetMap("hash",1536,false);
+ mask=GetMap("mt",pData->MTLoadSize,false);
+ if(!hash || !mask) return false;
+ return true;
+}
+
+cFileMap *cSeca2Prov::GetMap(const char *type, int size, bool generic) const
+{
+ char name[32];
+ snprintf(name,sizeof(name),generic ? "s2_%s.bin" : "s2_%s_%04x.bin",type,pData->LoadId);
+ cFileMap *map=filemaps.GetFileMap(name,FILEMAP_DOMAIN,false);
+ if(!map)
+ PRINTF(L_SYS_CRYPTO,"no filemap for %s",name);
+ else if(!map->Map()) {
+ PRINTF(L_SYS_CRYPTO,"mapping failed for %s",name);
+ map=0;
+ }
+ else if(map->Size()<size) {
+ PRINTF(L_SYS_CRYPTO,"%s file %s has wrong size (has=%d wanted=%d)",type,name,map->Size(),size);
+ map->Unmap();
+ map=0;
+ }
+ return map;
+}
+
+// Hash file layout is (Yankse style):
+// Table T1 for 9x,
+// Table T1 for Bx,
+// Table T1 for FX,
+// Table T2 for 9x,
+// Table T2 for Bx,
+// Table T2 for FX (total: 1536 bytes)
+
+const unsigned char *cSeca2Prov::T1(int index) const
+{
+ static int idxTrans[] = { 0,1,2,2 };
+ if(index>=0 && index<=3) {
+ return hash->Addr()+(idxTrans[index]*256);
+ }
+ PRINTF(L_SYS_CRYPTO,"bad T1 table index %d",index);
+ return 0;
+}
+
+const unsigned char *cSeca2Prov::T2(int index) const
+{
+ static int idxTrans[] = { 0,1,2,2 };
+ if(index>=0 && index<=3) {
+ return hash->Addr()+(idxTrans[index]*256)+768;
+ }
+ PRINTF(L_SYS_CRYPTO,"bad T2 table index %d",index);
+ return 0;
+}
+
+const struct Perm *cSeca2Prov::P(int index) const
+{
+ if(index>=0 && index<MAX_PERMS && perm) {
+ return &perm[index];
+ }
+ PRINTF(L_SYS_CRYPTO,"no perm table or bad index %d",index);
+ return 0;
+}
+
+unsigned short cSeca2Prov::Sum(const unsigned char *data, int n) const
+{
+ unsigned int sum=0;
+ for(int i=1; i<n; i+=2) sum+=((data[i-1]<<8)+data[i]);
+ if(n&1) sum+=(data[n-1]<<4);
+ return sum&0x3FFF;
+}
+
+bool cSeca2Prov::DecryptSE(unsigned char *data, const unsigned char *key, int n, int start, const unsigned char *T1, const unsigned char *T2)
+{
+ const int sigStart=n-data[n-1]-9; // 82 <8 bytes> P5(sigStart)
+ const int encrLen=sigStart-16;
+
+#ifdef DEBUG_SECA_EXTRA
+ if(encrLen>=n || encrLen<0) printf("encrLen error in SE\n");
+ if(sigStart<16) printf("sigStart error in SE\n");
+#endif
+ if(encrLen>=n || encrLen<0 || sigStart<16) return false;
+
+ Decrypt(data+sigStart-8,key,T1,T2);
+ ChainTableXor(data+sigStart-8,Sum(data+start,sigStart-8-start));
+ int i;
+
+ for(i=start; i<encrLen; i+=8) {
+ Decrypt(data+i,key,T1,T2);
+ ChainTableXor(data+i,Sum(&data[i+8],8));
+ }
+
+ int restBytes=sigStart&0x07;
+ if(!restBytes) restBytes=8;
+
+ // Last Block
+ unsigned char lastBlock[8];
+ memset(lastBlock,0,sizeof(lastBlock));
+ memcpy(lastBlock,data+sigStart-restBytes,restBytes);
+ Decrypt(lastBlock,key,T1,T2);
+
+ Decrypt(data+i,key,T1,T2);
+ ChainTableXor(data+i,Sum(lastBlock,sizeof(lastBlock)));
+ return true;
+}
+
+void cSeca2Prov::CalcSHASignature(const unsigned char *data, int n, unsigned char *signature, bool PadMode)
+{
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx,data,n);
+ unsigned char Pad=0;
+ if(PadMode) {
+ unsigned char End=pData->SHA1End;
+ SHA1_Update(&ctx,&End,1);
+ int l=(n&63)+1;
+ Pad=pData->SHA1Pad;
+ if(l>62) {
+ for(; l<64; l++) SHA1_Update(&ctx,&Pad,1);
+ l=0;
+ }
+ for(; l<62; l++) SHA1_Update(&ctx,&Pad,1);
+ unsigned short s=bswap_16(n);
+ SHA1_Update(&ctx,&s,2);
+ }
+ else for(; n&63; n++) SHA1_Update(&ctx,&Pad,1);
+
+ *((unsigned int *)(signature ))=bswap_32(ctx.h0);
+ *((unsigned int *)(signature+ 4))=bswap_32(ctx.h1);
+ *((unsigned int *)(signature+ 8))=bswap_32(ctx.h2);
+ *((unsigned int *)(signature+12))=bswap_32(ctx.h3);
+ *((unsigned int *)(signature+16))=bswap_32(ctx.h4);
+}
+
+// -- cSecaProviders & cSecaProviderLink ---------------------------------------
+
+class cSeca2ProvLink {
+friend class cSeca2Providers;
+private:
+ cSeca2ProvLink *next;
+ const unsigned short *ids;
+public:
+ cSeca2ProvLink(const unsigned short *Ids);
+ bool CanHandle(unsigned short Id);
+ virtual cSeca2Prov *Create(unsigned short Id)=0;
+ virtual ~cSeca2ProvLink() {};
+ };
+
+template<class CC> class cSeca2ProvLinkReg : public cSeca2ProvLink {
+public:
+ cSeca2ProvLinkReg(const unsigned short *Ids):cSeca2ProvLink(Ids) {}
+ virtual cSeca2Prov *Create(unsigned short Id) { return new CC(Id); }
+ };
+
+class cSeca2Providers {
+friend class cSeca2ProvLink;
+private:
+ static cSeca2ProvLink *first;
+ //
+ static void Register(cSeca2ProvLink *spl);
+public:
+ static cSeca2Prov *GetProv(unsigned short id);
+ };
+
+cSeca2ProvLink *cSeca2Providers::first=0;
+
+void cSeca2Providers::Register(cSeca2ProvLink *spl)
+{
+ LBSTART(L_CORE_DYN);
+ LBPUT("seca2prov: registering Seca2 provider");
+ for(int i=0; spl->ids[i]; i++) LBPUT(" %.4x",spl->ids[i]);
+ LBEND();
+ spl->next=first;
+ first=spl;
+}
+
+cSeca2Prov *cSeca2Providers::GetProv(unsigned short id)
+{
+ cSeca2ProvLink *spl=first;
+ while(spl) {
+ if(spl->CanHandle(id)) {
+ cSeca2Prov *sp=spl->Create(id);
+ if(sp && sp->Init()) return sp;
+ delete sp;
+ }
+ spl=spl->next;
+ }
+ return 0;
+}
+
+cSeca2ProvLink::cSeca2ProvLink(const unsigned short *Ids)
+{
+ ids=Ids;
+ cSeca2Providers::Register(this);
+}
+
+bool cSeca2ProvLink::CanHandle(unsigned short Id)
+{
+ for(int i=0; ids[i]; i++) if(ids[i]==Id) return true;
+ return false;
+}
+
+// -- cSeca2ProvSSE ------------------------------------------------------------
+
+#define beforeSig (pos-8)
+#define afterSig (pos+1)
+
+class cSeca2ProvSSE : public cSeca2Prov {
+private:
+ virtual void PreSSECore(unsigned char *buf, const unsigned char *data, int i)=0;
+ virtual void PostSSECore1(unsigned char *data, int pos)=0;
+ virtual void PostSSECore2(unsigned char *buf, const unsigned char *data, int pos)=0;
+ virtual void PostSSECore3(unsigned char *data, const unsigned char *buf, int pos)=0;
+ virtual void PostSSECore4(unsigned char *data, int pos)=0;
+protected:
+ cDes des;
+ cFileMap *sse, *sseP, *cw;
+ //
+ virtual bool InitSSE(void)=0;
+ virtual void PreSSE(unsigned char *data, int pos);
+ virtual void PostSSE(unsigned char *data, int pos);
+ //
+ inline const unsigned char *SSET1(void) const { return sse->Addr(); }
+ inline const unsigned char *SSET2(void) const { return sse->Addr()+3072; }
+ inline const unsigned char *SSEPT1(void) const { return sseP->Addr(); }
+ inline const unsigned char *SSEPT2(void) const { return sseP->Addr()+80; }
+ inline const unsigned char *CWT1(void) const { return cw->Addr(); }
+public:
+ cSeca2ProvSSE(unsigned short Id);
+ virtual ~cSeca2ProvSSE();
+ virtual bool Init(void);
+ };
+
+cSeca2ProvSSE::cSeca2ProvSSE(unsigned short Id)
+:cSeca2Prov(Id)
+,des(secaPC1,secaPC2)
+{
+ sse=0; sseP=0; cw=0;
+}
+
+cSeca2ProvSSE::~cSeca2ProvSSE()
+{
+ if(sse) sse->Unmap();
+ if(sseP) sseP->Unmap();
+ if(cw) cw->Unmap();
+}
+
+bool cSeca2ProvSSE::Init(void)
+{
+ return InitSSE() && cSeca2Prov::Init();
+}
+
+void cSeca2ProvSSE::PreSSE(unsigned char *data, int pos)
+{
+ const unsigned char *T1=SSEPT1();
+ unsigned char tmpBuf[80];
+
+ data+=pos+5; // Start at offset 5
+ for(int i=4; i>=0; i--) {
+ int j=i*16;
+ PreSSECore(&tmpBuf[j],&data[j],i);
+ }
+ for(int i=79; i>=0; i--) data[i]=tmpBuf[i]^T1[i];
+}
+
+void cSeca2ProvSSE::PostSSE(unsigned char *data, int pos)
+{
+ PostSSECore1(data,pos);
+ // create the SHA hash buffer
+ unsigned char tmpBuf[64];
+ memcpy(tmpBuf+0,&data[beforeSig+0],4);
+ memcpy(tmpBuf+4,&data[afterSig +4],4);
+ PostSSECore2(tmpBuf+8,data,pos);
+ // Calc Signature of the generated buffer here
+ unsigned char MD[20];
+ CalcSHASignature(tmpBuf,sizeof(tmpBuf),MD,false);
+ // Prepare DES data
+ memcpy(tmpBuf+0,&data[beforeSig+4],4);
+ memcpy(tmpBuf+4,&data[afterSig +0],4);
+ // DES Enrypt
+ des.Des(tmpBuf,MD,SECA_DES_ENCR);
+ // modify data with encrypted DES data
+ PostSSECore3(data,tmpBuf,pos);
+ // save the signature
+ memcpy(tmpBuf,data+afterSig,8);
+ PostSSECore4(data,pos);
+ // put the signature in the data
+ memcpy(data+beforeSig,tmpBuf,8);
+}
+
+// -- France -------------------------------------------------------------------
+
+class cSeca2ProvFR : public cSeca2ProvSSE {
+private:
+ virtual void PreSSECore(unsigned char *buf, const unsigned char *data, int i);
+ virtual void PostSSECore1(unsigned char *data, int pos);
+ virtual void PostSSECore2(unsigned char *buf, const unsigned char *data, int pos);
+ virtual void PostSSECore3(unsigned char *data, const unsigned char *buf, int pos);
+ virtual void PostSSECore4(unsigned char *data, int pos);
+protected:
+ virtual bool InitSSE(void);
+ virtual void PostCW(unsigned char *data);
+ virtual void ChainTableXor(unsigned char *data, unsigned short index);
+ virtual void SignatureMod(unsigned char *MD, const unsigned char *PK);
+public:
+ cSeca2ProvFR(unsigned short Id);
+ };
+
+static const unsigned short IdsFR[] = { 0x80,0x81,0 };
+
+static cSeca2ProvLinkReg<cSeca2ProvFR> staticLinkFR(IdsFR);
+
+static const struct ProvData provDataFR = {
+ 0x80,16895,16895,0x7F,0xF7, { 3, 0, 0, 0, 2, 4, 3, 0 }
+ };
+
+cSeca2ProvFR::cSeca2ProvFR(unsigned short Id)
+:cSeca2ProvSSE(Id)
+{
+ pData=&provDataFR;
+}
+
+bool cSeca2ProvFR::InitSSE(void)
+{
+ sse=GetMap("sse",5120,true);
+ sseP=GetMap("sse",1104,false);
+ cw=GetMap("cw",512,false);
+ return sse && sseP && cw;
+}
+
+void cSeca2ProvFR::PreSSECore(unsigned char *buf, const unsigned char *data, int i)
+{
+ const unsigned char *T1=SSET1(), *T2=SSET2();
+
+ buf[ 0]=data[0x06]^T2[i+0x0a];
+ buf[ 1]=data[0x01]^T2[i+0x00];
+ buf[ 2]=data[0x02]^T1[i+0x00];
+ buf[ 3]=data[0x03]^T2[i+0x04];
+ buf[ 4]=~data[0x04];
+ buf[ 5]=data[0x05]^T1[i+0x01];
+ buf[ 6]=data[0x00]^T2[i+0x0f];
+ buf[ 7]=data[0x07]^T1[i+0x02];
+ buf[ 8]=data[0x08]^T1[i+0x00];
+ buf[ 9]=data[0x0d]^T2[i+0x14];
+ buf[10]=data[0x0f]^T2[i+0x07];
+ buf[11]=~data[0x0b];
+ buf[12]=data[0x0c]^T1[i+0x00];
+ buf[13]=data[0x09]^T2[i+0x19];
+ buf[14]=data[0x0e]^T2[i+0x1e];
+ buf[15]=data[0x0a]^T1[i+0x01];
+}
+
+void cSeca2ProvFR::PostSSECore1(unsigned char *data, int pos)
+{
+ data[beforeSig+0]^=-(0x2d);
+ data[beforeSig+1]-=0x22;
+ data[beforeSig+2]^=0x1d;
+ data[beforeSig+3]^=-(0x68);
+ data[beforeSig+4] =~data[beforeSig + 4];
+ data[beforeSig+5]^=0x26;
+ data[beforeSig+6]^=0x09;
+ data[beforeSig+7]-=0x3e;
+ data[afterSig +0]^=-(0x5d);
+ data[afterSig +1]-=0x74;
+ data[afterSig +2]^=0x2d;
+ data[afterSig +3]^=-(0x2a);
+ data[afterSig +4]+=0x0d;
+ data[afterSig +5]^=-(0x6c);
+ data[afterSig +6]^=-(0x76);
+ data[afterSig +7]+=0x31;
+}
+
+void cSeca2ProvFR::PostSSECore2(unsigned char *buf, const unsigned char *data, int pos)
+{
+ const unsigned char *T1=SSEPT2();
+ memcpy(buf, &T1[data[afterSig+4]+0x48], 56);
+}
+
+void cSeca2ProvFR::PostSSECore3(unsigned char *data, const unsigned char *buf, int pos)
+{
+ data[beforeSig+4]=buf[5];
+ data[beforeSig+5]=buf[4];
+ data[beforeSig+6]=buf[7];
+ data[beforeSig+7]=buf[2];
+ data[afterSig +0]=buf[3];
+ data[afterSig +1]=buf[1];
+ data[afterSig +2]=buf[0];
+ data[afterSig +3]=buf[6];
+}
+
+void cSeca2ProvFR::PostSSECore4(unsigned char *data, int pos)
+{
+ data[afterSig+0]=data[beforeSig+3]^0x3e;
+ data[afterSig+1]=data[beforeSig+1]^0x5e;
+ data[afterSig+2]=data[beforeSig+5]^0x2f;
+ data[afterSig+3]=data[beforeSig+0]^0x77;
+ data[afterSig+4]=data[beforeSig+6]^-(0x4b);
+ data[afterSig+5]=data[beforeSig+2]^-(0x38);
+ data[afterSig+6]=data[beforeSig+7]^0x29;
+ data[afterSig+7]=data[beforeSig+4]^0x2b;
+}
+
+void cSeca2ProvFR::PostCW(unsigned char *data)
+{
+ const unsigned char *T1=SSET1(), *T2=SSET2(), *T3=SSEPT2(), *T4=CWT1();
+ unsigned int idx;
+ unsigned char key[8];
+ idx=((data[0]<<8)|data[1]); key[0]=T3[idx & 0x3FF];
+ idx=(idx + key[0]); key[1]=T3[idx & 0x3FF];
+ idx=((data[2]<<8)|data[3]); key[2]=T4[idx & 0x1FF];
+ idx=idx + key[2]; key[3]=T4[idx & 0x1FF];
+ idx=((data[8+4]<<8)|data[8+5]); key[4]=T2[idx & 0x7FF];
+ idx=idx + key[4]; key[5]=T2[idx & 0x7FF];
+ idx=((data[8+6]<<8)|data[8+7]); key[6]=T1[idx & 0xBFF];
+ idx=idx + key[6]; key[7]=T1[idx & 0xBFF];
+ des.Des(data+4,key,SECA_DES_ENCR);
+}
+
+void cSeca2ProvFR::ChainTableXor(unsigned char *data, unsigned short index)
+{
+ static const unsigned char tabIdx[] = { 0x00, 0x08, 0x03, 0x1F, 0x06, 0x32, 0x12, 0x0C };
+ static const unsigned char tabXor[] = { 0x77, 0x2B, 0xC8, 0xEE, 0x2F, 0xD3, 0x22, 0x29 };
+ static const unsigned char tabPos[] = { 0, 2, 1, 6, 4, 5, 7, 3 };
+
+ unsigned int idx1, idx2;
+ unsigned char xorVal=0;
+ const unsigned char *T1=MT(), *T2=SSET1();
+
+ idx1 = (index^0x17AC) & 0x3FFF;
+ idx2 = idx1 & 0xBFF;
+ for(int i=0; i<8; i++) {
+ idx1 = (idx1 + tabIdx[i]) & 0x3FFF;
+ idx2 = (idx2 + xorVal) & 0xBFF;
+ xorVal ^= T1[idx1] ^ tabXor[i];
+ data[tabPos[i]] ^= xorVal ^ T2[idx2];
+ }
+}
+
+void cSeca2ProvFR::SignatureMod(unsigned char *MD, const unsigned char *PK)
+{
+ const unsigned char *T1=SSEPT2();
+ unsigned int idx;
+
+ idx = ((MD[18] << 8) | MD[19]) & 0x3FF;
+ MD[10] = T1[idx];
+ MD[13] ^= PK[6];
+ idx = ((MD[15] << 8) | MD[13]) & 0x3FF;
+ MD[16] = T1[idx];
+ des.Des(MD,MD+10,SECA_DES_ENCR);
+}
+
+// -- Netherlands --------------------------------------------------------------
+
+class cSeca2ProvNL : public cSeca2ProvSSE {
+private:
+ virtual void PreSSECore(unsigned char *buf, const unsigned char *data, int i);
+ virtual void PostSSECore1(unsigned char *data, int pos);
+ virtual void PostSSECore2(unsigned char *buf, const unsigned char *data, int pos);
+ virtual void PostSSECore3(unsigned char *data, const unsigned char *buf, int pos);
+ virtual void PostSSECore4(unsigned char *data, int pos);
+protected:
+ virtual bool InitSSE(void);
+ virtual void PostCW(unsigned char *data);
+ virtual void ChainTableXor(unsigned char *data, unsigned short index);
+ virtual bool DoSigCheck(void) { return false; }
+public:
+ cSeca2ProvNL(unsigned short Id);
+ };
+
+static const unsigned short IdsNL[] = { 0x6a,0 };
+
+static cSeca2ProvLinkReg<cSeca2ProvNL> staticLinkNL(IdsNL);
+
+static const struct ProvData provDataNL = {
+ 0x6a,16895,16895,0xa3,0x3a, { 3, 0, 0, 0, 2, 4, 3, 0 }
+ };
+
+cSeca2ProvNL::cSeca2ProvNL(unsigned short Id)
+:cSeca2ProvSSE(Id)
+{
+ pData=&provDataNL;
+}
+
+bool cSeca2ProvNL::InitSSE(void)
+{
+ sse=GetMap("sse",5120,true);
+ sseP=GetMap("sse",336,false);
+ cw=GetMap("cw",512,false);
+ return sse && sseP && cw;
+}
+
+void cSeca2ProvNL::PreSSECore(unsigned char *buf, const unsigned char *data, int i)
+{
+ const unsigned char *T1=SSET1(), *T2=SSET2();
+
+ buf[0] =data[7] ^T1[i+0x17];
+ buf[1] =data[1] ^T2[i+0x07];
+ buf[2] =data[2] ^T1[i+0x03];
+ buf[3] =data[3] ^T2[i+0x0C];
+ buf[4] =~data[4];
+ buf[5] =data[5] ^T1[i+0x1E];
+ buf[6] =data[6] ^T1[i+0x11];
+ buf[7] =data[0] ^T2[i+0x0F];
+ buf[8] =data[9] ^T1[i+0x15];
+ buf[9] =data[8] ^T2[i+0x04];
+ buf[10]=data[12]^T2[i+0x07];
+ buf[11]=data[11]^T1[i+0x16];
+ buf[12]=data[10]^T1[i+0x01];
+ buf[13]=data[13]^T1[i+0x0F];
+ buf[14]=~data[14];
+ buf[15]=data[15]^T2[i+0x0B];
+}
+
+void cSeca2ProvNL::PostSSECore1(unsigned char *data, int pos)
+{
+ // modify 8 bytes before signature byte (0x82)
+ data[beforeSig+0] ^= 0xd6; data[beforeSig+1] ^= 0x96;
+ data[beforeSig+2] -= 0x51; data[beforeSig+3] ^= 0x3a;
+ data[beforeSig+4] -= 0x8d; data[beforeSig+5] ^= 0xf1;
+ data[beforeSig+6] -= 0xc2; data[beforeSig+7] ^= 0xb1;
+
+ data[afterSig+0] ^= 0x84; data[afterSig+1] ^= 0xf8;
+ data[afterSig+2] -= 0x7d; data[afterSig+3] = ~(data[afterSig+3]);
+ data[afterSig+4] ^= 0xfd; data[afterSig+5] ^= 0xd0;
+ data[afterSig+6] ^= 0x77; data[afterSig+7] ^= 0x25;
+}
+
+void cSeca2ProvNL::PostSSECore2(unsigned char *buf, const unsigned char *data, int pos)
+{
+ const unsigned char *T1=SSEPT2();
+ memcpy(buf,&T1[(data[afterSig+6]+0x19)&0x7F],56);
+}
+
+void cSeca2ProvNL::PostSSECore3(unsigned char *data, const unsigned char *buf, int pos)
+{
+ data[beforeSig+4]=buf[7];
+ data[beforeSig+5]=buf[0];
+ data[beforeSig+6]=buf[3];
+ data[beforeSig+7]=buf[5];
+ data[afterSig+0]=buf[6];
+ data[afterSig+1]=buf[2];
+ data[afterSig+2]=buf[1];
+ data[afterSig+3]=buf[4];
+}
+
+void cSeca2ProvNL::PostSSECore4(unsigned char *data, int pos)
+{
+ data[afterSig+0]=data[beforeSig+1] ^ 0x65; data[afterSig+1]=data[beforeSig+0] ^ 0x75;
+ data[afterSig+2]=data[beforeSig+5] ^ 0x35; data[afterSig+3]=data[beforeSig+3] ^ 0xd9;
+ data[afterSig+4]=data[beforeSig+6] ^ 0xb7; data[afterSig+5]=data[beforeSig+7] ^ 0x9a;
+ data[afterSig+6]=data[beforeSig+4] ^ 0xc7; data[afterSig+7]=data[beforeSig+2] ^ 0x1f;
+}
+
+void cSeca2ProvNL::PostCW(unsigned char *data)
+{
+ const unsigned char *T1=SSET1(), *T2=SSET2(), *T3=CWT1(), *T4=SSEPT2();
+ unsigned char key[8];
+ unsigned int off2;
+
+ off2=data[2]^data[8+6];
+ key[2]=T4[off2]; key[5]=T4[(off2+key[2])&0xff];
+
+ off2=(data[3]<<8)|data[8+4]; key[0]=T3[off2&0x1ff];
+ off2+=key[2]; key[4]=T3[off2&0x1ff];
+
+ off2=(data[0]<<8)|data[8+7]; key[7]=T2[off2&0x7ff];
+ off2+=key[0]; key[1]=T2[off2&0x7ff];
+
+ off2=(data[1]<<8)|data[8+5]; key[3]=T1[off2&0xbff];
+ off2+=key[4]; key[6]=T1[off2&0xbff];
+
+ des.Des(data+4,key,SECA_DES_ENCR);
+}
+
+void cSeca2ProvNL::ChainTableXor(unsigned char *data, unsigned short index)
+{
+ static const unsigned short tabIdx[] = { 0x000, 0x4C3, 0x5D8, 0x63A, 0x471, 0x639, 0x417, 0x6CD };
+ static const unsigned char tabXor[] = { 0x84, 0xD6, 0x3A, 0x1F, 0x25, 0xB1, 0x7D, 0xF7 };
+ static const unsigned char tabPos1[] = { 2, 4, 3, 4, 5, 7, 6, 7 };
+ static const unsigned char tabPos2[] = { 3, 1, 5, 6, 0, 1, 0, 2 };
+
+ unsigned int idx1, idx2;
+ unsigned char xorVal=0;
+ const unsigned char *T1=MT(), *T2=SSET1();
+
+ idx1=(index^0x2B36) & 0x3FFF;
+ idx2=idx1 & 0xBFF;
+ for(int i=0; i<8; i++) {
+ idx1=(idx1 + tabIdx[i]) & 0x3FFF;
+ idx2=(idx2 + xorVal) & 0xBFF;
+ xorVal ^= T1[idx1] ^ tabXor[i];
+ data[tabPos1[i]]^=xorVal;
+ data[tabPos2[i]]^=T2[idx2];
+ }
+}
+
+#undef beforeSig
+#undef afterSig
+
+// -- Poland -------------------------------------------------------------------
+
+class cSeca2ProvPL : public cSeca2Prov {
+protected:
+ virtual void PreCW(unsigned char *data);
+ virtual void ChainTableXor(unsigned char *data, unsigned short index);
+public:
+ cSeca2ProvPL(unsigned short Id);
+ };
+
+static const unsigned short IdsPL[] = { 0x65,0 };
+
+static cSeca2ProvLinkReg<cSeca2ProvPL> staticLinkPL(IdsPL);
+
+static const struct ProvData provDataPL = {
+ 0x65,16895,16895,0x96,0x69, { 3, 0, 0, 0, 2, 4, 3, 0 }
+ };
+
+static const struct Perm ptPL[MAX_PERMS] = {
+ { // 1
+ { 2, 4, 4, 3, 2, 2, 4, 4 },
+ { 3, 2, 4, 3, 4, 4, 2, 3 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },{ // 2
+ { 4, 4, 3, 2, 2, 3, 3, 4 },
+ { 4, 2, 3, 4, 3, 2, 4, 2 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 }
+ },{ // 3
+ },{ // 4
+ },{ // 5
+ },{ // 6
+ },{ // 7
+ },{ // 8
+ },{ // 9
+ { 4, 4, 3, 2, 3, 4, 3, 2 },
+ { 2, 4, 3, 2, 2, 4, 4, 3 },
+ { 2, 3, 4, 1, 0, 0, 0, 0 },
+ { 0, 0, 0, 0, 0, 0, 0, 0 },
+ },{ // 10
+ } };
+
+cSeca2ProvPL::cSeca2ProvPL(unsigned short Id)
+:cSeca2Prov(Id)
+{
+ pData=&provDataPL; perm=ptPL;
+}
+
+void cSeca2ProvPL::PreCW(unsigned char *data)
+{
+ static const unsigned char XT[]={ 0xA0,0x12,0x23,0x35,0x46,0xB0,0xDF,0xCA, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
+ static const unsigned char PT[]={ 0x05,0x04,0x06,0x07,0x03,0x00,0x01,0x02, 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F };
+ unsigned char temp[16];
+
+ xxor(temp,16,data,XT);
+ for(int i=15; i>=0; i--) data[i]=temp[PT[i]];
+}
+
+void cSeca2ProvPL::ChainTableXor(unsigned char *data, unsigned short index)
+{
+ const unsigned char *T1=MT();
+ for(int i=0; i<8; i++) data[i]^=T1[index++];
+}
+
+// -- cSystemSeca ---------------------------------------------------------------
+
+class cSystemSeca : public cSystem, private cSeca {
+private:
+ cSeca2Prov *sp, *spL;
+ int emmLenCnt;
+ cDes des;
+ cRSA rsa;
+ //
+ bool GetSeca2Prov(int provId, cSeca2Prov * &sp);
+ bool Process0FNano(int count, unsigned char *hashDW, unsigned char *PK, const unsigned char *T1, const unsigned char *T2);
+ bool Process51Nano(unsigned char *CW, const unsigned char *PermData, const unsigned char *hashDW, const unsigned char *T1, const unsigned char *T2, int provId);
+ void Crypto51Nano(unsigned char *data, const unsigned char *key, const unsigned char crypto, const unsigned char mode);
+ void Permute(unsigned char *data, const unsigned char *pdata, const unsigned char *P);
+public:
+ cSystemSeca(void);
+ virtual ~cSystemSeca();
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+ virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+ };
+
+cSystemSeca::cSystemSeca(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI),
+des(secaPC1,secaPC2)
+{
+ sp=spL=0; emmLenCnt=0;
+ hasLogger=true;
+}
+
+cSystemSeca::~cSystemSeca()
+{
+ delete sp;
+ delete spL;
+}
+
+bool cSystemSeca::GetSeca2Prov(int provId, cSeca2Prov * &sp)
+{
+ if(!sp || !sp->Matches(provId)) {
+ delete sp;
+ sp=cSeca2Providers::GetProv(provId);
+ }
+ return sp!=0;
+}
+
+bool cSystemSeca::ProcessECM(const cEcmInfo *ecmD, unsigned char *data)
+{
+ bool SE=false;
+ unsigned char *ecm;
+ const int msgLen=cParseSeca::Payload(data,(const unsigned char **)&ecm);
+ const int keyNr=cParseSeca::KeyNr(data);
+
+ if(keyNr&0x80) {
+ PRINTF(L_SYS_VERBOSE,"Super Encryption enabled");
+ SE=true;
+ if(!GetSeca2Prov(ecmD->provId,sp)) {
+ if(doLog) PRINTF(L_SYS_ECM,"Seca2 provider %04x not supported",ecmD->provId);
+ return false;
+ }
+ }
+ HEXDUMP(L_SYS_VERBOSE,data,SCT_LEN(data),"ECM:");
+ if(ecm[0]==0x10) {
+ if(msgLen<0x5c) {
+ if(doLog) PRINTF(L_SYS_ECM,"ECM length too small (%d)",msgLen);
+ return false;
+ }
+
+ if(SE) {
+ HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM before SSE data modification :");
+ sp->PreSSE(ecm,msgLen-0x5a);
+ HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM after SSE data modification :");
+ }
+
+ const int rsaKeynr=(ecm[1]&0x0F)+'0';
+ cBN exp, mod;
+ cPlainKey *rsaKey;
+ if(!(rsaKey=keys.FindKey('S',ecmD->provId,MBC('E',rsaKeynr),-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04x E%c key",ecmD->provId,rsaKeynr);
+ return false;
+ }
+ rsaKey->Get(exp);
+ if(!(rsaKey=keys.FindKey('S',ecmD->provId,MBC('M',rsaKeynr),-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04x M%c key",ecmD->provId,rsaKeynr);
+ return false;
+ }
+ rsaKey->Get(mod);
+ HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM before RSA decrypt :");
+ if(rsa.RSA(ecm+(msgLen-0x5a),ecm+(msgLen-0x5a),0x5a,exp,mod)!=0x5a) {
+ PRINTF(L_SYS_CRYPTO,"RSA decrypt failed (ECM)");
+ return false;
+ }
+ HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM after RSA decrypt :");
+
+ if(SE) { // PostSSE needed
+ const int hashPtr=msgLen-ecm[msgLen-1]-9;
+ if(hashPtr>=msgLen-9 || ecm[hashPtr]!=0x82) {
+ PRINTF(L_SYS_CRYPTO,"RSA decrypt failed, signature pointer not found");
+ return false;
+ }
+ HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM before SSE result modification :");
+ sp->PostSSE(ecm,hashPtr);
+ HEXDUMP(L_SYS_VERBOSE,ecm+(msgLen-0x5a),0x5a,"ECM after SSE result modification :");
+ }
+ }
+
+ const unsigned char *T1=T1_S1, *T2=T2_S1;
+ int decrLen=msgLen;
+ if(SE) {
+ if(!(T1=sp->T1(KEY2INDEX(keyNr))) ||
+ !(T2=sp->T2(KEY2INDEX(keyNr)))) return false;
+ decrLen-=ecm[msgLen-1]; if(decrLen<8) return false;
+ }
+
+ bool key8=!(cParseSeca::SysMode(data)&0x10);
+ cPlainKey *pk=0;
+ cKeySnoop ks(this,'S',ecmD->provId,keyNr&0x0F);
+ while((pk=keys.FindKey('S',ecmD->provId,keyNr&0x0F,key8?8:16,pk))) {
+ unsigned char buff[msgLen];
+ memcpy(buff,ecm,sizeof(buff)); // if decoding fails we need the original data
+
+ unsigned char PK[16], signature[20];
+ pk->Get(PK);
+ if(key8) memcpy(&PK[8],&PK[0],8); // duplicate key
+
+ if(SE) {
+ sp->CalcSHASignature(buff,decrLen-8,signature);
+ LDUMP(L_SYS_VERBOSE,signature,8,"signature before encrypt :");
+ sp->SignatureMod(signature,PK);
+ Encrypt(signature,PK,T1,T2);
+ LDUMP(L_SYS_VERBOSE,signature,8,"signature after encrypt :");
+
+ if(sp->DoSigCheck() && memcmp(signature,&buff[decrLen-8],8)) {
+ PRINTF(L_SYS_VERBOSE,"signature check failed before SE decrypt");
+ continue;
+ }
+ HEXDUMP(L_SYS_VERBOSE,buff+2,msgLen-2,"ECM before SE decrypt :");
+ if(!sp->DecryptSE(buff+2,PK,msgLen-2,8,T1,T2)) {
+ PRINTF(L_SYS_VERBOSE,"SE decrypt failed");
+ continue;
+ }
+ HEXDUMP(L_SYS_VERBOSE,buff+2,msgLen-2,"ECM after SE decrypt :");
+ }
+ else {
+ CalcSignature(buff,decrLen-8,signature,PK,T1,T2);
+ }
+ unsigned char hashDW[28]; // actually 16 bytes used for processing, but SHA needs more
+ unsigned char *cCW=0;
+ const unsigned char *nano51Data=0;
+ int nr0F=0;
+
+ for(int i=0 ; i<decrLen; ) {
+ int param=buff[i++];
+ int extra=(param >> 4) & 0x0f;
+ switch(extra) {
+ case 0x0d: extra=0x10; break;
+ case 0x0e: extra=0x18; break;
+ case 0x0f: extra=0x20; break;
+ }
+ switch(param) {
+ case 0x0f:
+ if(SE) nr0F++;
+ break;
+ case 0x51:
+ if(SE) nano51Data=&buff[i];
+ break;
+ case 0xd1:
+ cCW=&buff[i];
+ if(SE) {
+ sp->CalcSHASignature(buff,i,hashDW,false);
+ memcpy(&hashDW[20],hashDW,8);
+ Decrypt(&hashDW[20],PK,sp->T1(0),sp->T2(0));
+ sp->CalcSHASignature(hashDW,28,hashDW,false);
+ }
+ break;
+ case 0x82:
+ if((!SE || sp->DoSigCheck()) && memcmp(&buff[i],signature,8)) {
+ PRINTF(L_SYS_VERBOSE,"signature check failed after decrypt");
+ cCW=0; i=decrLen;
+ }
+ break;
+ }
+ i+=extra;
+ }
+ if(cCW) {
+ if(!nr0F || Process0FNano(nr0F,hashDW,PK,T1,T2)) {
+ if(SE) sp->PreCW(cCW);
+ if(!nano51Data || Process51Nano(cCW,nano51Data,hashDW,T1,T2,ecmD->provId)) {
+ LDUMP(L_SYS_VERBOSE,cCW,16,"crypted CW :");
+ Decrypt(&cCW[0],PK,T1,T2); Decrypt(&cCW[8],PK,T1,T2);
+ LDUMP(L_SYS_VERBOSE,cCW,16,"decrypted CW :");
+ if(SE) {
+ LDUMP(L_SYS_VERBOSE,cCW,16,"decrypted CW before modification :");
+ sp->PostCW(cCW);
+ LDUMP(L_SYS_VERBOSE,cCW,16,"decrypted CW after modification :");
+ }
+ memcpy(cw,cCW,16);
+ ks.OK(pk);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool cSystemSeca::Process0FNano(int count, unsigned char *hashDW, unsigned char *PK, const unsigned char *T1, const unsigned char *T2)
+{
+ const unsigned char *MT=sp->MT();
+ unsigned char buffB[8];
+
+ LDUMP(L_SYS_VERBOSE,hashDW,16,"0f: initial hashDW:");
+ LDUMP(L_SYS_VERBOSE,PK,16,"0f: PK:");
+ while(count--) {
+ unsigned char buffA[8];
+ swap8_4(hashDW);
+ memcpy(buffA,hashDW,8);
+ Decrypt(hashDW,PK,sp->T1(0),sp->T2(0));
+ LDUMP(L_SYS_VERBOSE,hashDW,16,"0f: hashDW:");
+
+ unsigned char buffE[8];
+ int off=WORD(hashDW,0,0x3FFF);
+ xxor(buffE,8,&MT[off],&MT[0x3FFF-off]);
+ xxor(buffE,8,&MT[( ((buffE[0] ^ hashDW[6])<<8)
+ + buffE[7] ^ hashDW[7] )&0x3FFF],buffE);
+ LDUMP(L_SYS_VERBOSE,buffE,8,"0f: buffE:");
+
+ unsigned char buffC[16];
+ off=WORD(hashDW,2,0x3FFF);
+ xxor(buffC,16,&MT[off],&MT[0x3FFF-off]);
+ xxor(buffC,16,&MT[( ((hashDW[6] ^ buffC[0])<<8)
+ + (hashDW[7] ^ buffC[15]) )&0x3FFF],buffC);
+ LDUMP(L_SYS_VERBOSE,buffC,16,"0f: buffC:");
+
+ off=WORD(hashDW,4,0x3FFF);
+ for(int i=0; i<16; ++i) off=WORD(MT,off,0x3FFF);
+ if(off+512 >= sp->MTSize()) {
+ printf("BUG!!!: error: masking table overflow\n");
+ return false;
+ }
+ PRINTF(L_SYS_VERBOSE,"0f: off=%04x",off);
+
+ memcpy(buffB,buffE,8);
+ Decrypt(buffB,buffC,&MT[off],&MT[off+256]);
+ LDUMP(L_SYS_VERBOSE,buffB,8,"0f: buffB after 1st decr:");
+ xxor(buffB,8,buffB,buffE);
+ LDUMP(L_SYS_VERBOSE,buffB,8,"0f: buffB after xor:");
+ Decrypt(buffB,PK,T1,T2);
+ LDUMP(L_SYS_VERBOSE,buffB,8,"0f: buffB after 2nd decr:");
+ xxor(hashDW,8,buffA,buffB);
+ }
+
+ for(int i=0; i<8; ++i) {
+ PK[i] = buffB[i];
+ PK[i+8] = ~sn8(buffB[i]);
+ }
+ LDUMP(L_SYS_VERBOSE,hashDW,16,"0f: hashDW:");
+ LDUMP(L_SYS_VERBOSE,PK,16,"0f: new PK:");
+ return true;
+}
+
+void cSystemSeca::Crypto51Nano(unsigned char *data, const unsigned char *key, const unsigned char crypto, const unsigned char mode)
+{
+ switch(crypto) {
+ case 0: // Xor
+ PRINTF(L_SYS_VERBOSE,"51: XOR crypto selected");
+ xxor(data+0,8,key,data+0); xxor(data+8,8,key,data+8);
+ break;
+ case 1: // Seca crypto, with 9x table, always!
+ {
+ unsigned char PK[16];
+ const unsigned char *T1 = sp->T1(0);
+ const unsigned char *T2 = sp->T2(0);
+ memcpy(PK+0,key,8); memcpy(PK+8,key,8);
+ PRINTF(L_SYS_VERBOSE,"51: SECA crypto selected: %s",mode?"decrypt":"encrypt");
+ if(mode) { Decrypt(data,PK,T1,T2); Decrypt(data+8,PK,T1,T2); }
+ else { Encrypt(data,PK,T1,T2); Encrypt(data+8,PK,T1,T2); }
+ break;
+ }
+ case 2: // DES crypto (Modified PC1,PC2)
+ PRINTF(L_SYS_VERBOSE,"51: DES crypto selected: %s",mode?"decrypt":"encrypt");
+ if(mode) { des.Des(data,key,SECA_DES_DECR); des.Des(data+8,key,SECA_DES_DECR); }
+ else { des.Des(data,key,SECA_DES_ENCR); des.Des(data+8,key,SECA_DES_ENCR); }
+ break;
+ case 3: // Additional Algo
+ PRINTF(L_SYS_VERBOSE,"51: Additional crypto selected: %s",mode?"decrypt":"encrypt");
+ AdditionalAlgo(data,key,mode); AdditionalAlgo(data+8,key,mode);
+ break;
+ }
+}
+
+bool cSystemSeca::Process51Nano(unsigned char *CW, const unsigned char *PermData, const unsigned char *hashDW, const unsigned char *T1, const unsigned char *T2, int provId)
+{
+ const int pMode=(PermData[0]&0x3F)-1;
+ const struct Perm *PT=sp->P(pMode);
+ if(!PT) {
+ PRINTF(L_SYS_VERBOSE,"51: failed to get permutation table");
+ return false;
+ }
+ unsigned char buffF[8], buffG[8];
+ switch(pMode) {
+ case 0x00:
+ case 0x01:
+ {
+ // Permutation 1
+ Permute(buffF,PermData,PT->P1);
+ LDUMP(L_SYS_VERBOSE,buffF,8,"51: buffF:");
+ xxor(buffG,8,buffF,hashDW);
+ const int addAlgoMode=(PermData[0]&1)^1;
+ for(int i=(PermData[1]&0x0f); i>0; i--) AdditionalAlgo(buffG,hashDW+8,addAlgoMode);
+ // Permutation 2
+ Permute(buffF,PermData,PT->P2);
+ xxor(buffG,8,buffF,buffG);
+ LDUMP(L_SYS_VERBOSE,buffG,8,"51: buffG:");
+ // Permutation 3
+ Permute(buffF,PermData,PT->P3);
+ xxor(buffG,8,buffF,buffG);
+ // Permutation 4
+ Permute(buffF,PermData,PT->P4);
+ xxor(buffG,8,buffF,buffG);
+ break;
+ }
+ case 0x08:
+ {
+ unsigned char buff1[128], buff2[64], buff3[9];
+ // prepare buffers for rsa data
+ for(int i=0; i<20; i++)
+ buff1[i]=buff2[i]=buff2[i+20]=buff2[i+40]=hashDW[i];
+ memcpy(buff3,PermData+2,3);
+ for(int i=0; i<8; i++) buff1[i] = buff3[PT->P1[i]-2] ^= buff1[i];
+ LDUMP(L_SYS_VERBOSE,buff1,20,"51: permuted hashDW:");
+ for(int i=0; i<20; i++)
+ buff1[i+24]=buff1[i+44]=buff1[i+68]=buff1[i+88]=buff1[i+108]=buff1[i];
+ LBSTARTF(L_SYS_VERBOSE);
+ LBPUT("51: nano51Data:");
+ for(int i=0; i<4; i++) {
+ unsigned char t=PermData[PT->P3[i]];
+ if(PT->P3[i]==1) t&=0x0F;
+ buff1[i+20]=buff1[i+64]=buff2[i+60]=t;
+ LBPUT(" %02x",t);
+ }
+ LBEND();
+ HEXDUMP(L_SYS_VERBOSE,buff1,128,"51: buff1:");
+ HEXDUMP(L_SYS_VERBOSE,buff2,64,"51: buff2:");
+
+ memcpy(buff3,buff1,9);
+ for(int j=0; j<64; j++) { // XOR even with buff2, odd with 0xFF
+ buff1[ j*2 ] ^= buff2[j];
+ buff1[(j*2)+1] ^= 0xFF;
+ }
+ HEXDUMP(L_SYS_VERBOSE,buff1,128,"51: buff1 rsa prepped:");
+
+ // RSA decrypt
+ {
+ cBN exp, mod;
+ cPlainKey *pk;
+ if(!(pk=keys.FindKey('S',provId,MBC3('E','9',N51_MAGIC),-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04x N51 E9 key",provId);
+ return false;
+ }
+ pk->Get(exp);
+ if(!(pk=keys.FindKey('S',provId,MBC3('M','9',N51_MAGIC),-1))) {
+ if(doLog) PRINTF(L_SYS_KEY,"missing %04x N51 M9 key",provId);
+ return false;
+ }
+ pk->Get(mod);
+ if(rsa.RSA(buff1,buff1,128,exp,mod)<9) return false;
+ HEXDUMP(L_SYS_VERBOSE,buff1,128,"51: buff1 rsa decrypted:");
+ }
+
+ unsigned int sum=0;
+ for(int j=0; j<9; j++) {
+ unsigned int nextSum=(buff1[j]&0x80) ? 1:0;
+ buff1[j]=(sum+2*buff1[j])^buff3[j];
+ sum=nextSum;
+ }
+ LDUMP(L_SYS_VERBOSE,buff1,9,"51: buff1 after sumalgo:");
+
+ memcpy(buffG,buff1,8);
+ memcpy(buff3,PermData+2,3);
+ for(int i=0; i<8; i++) buffG[i] = buff3[PT->P2[i]-2] ^= buffG[i];
+ break;
+ }
+
+ default:
+ PRINTF(L_SYS_VERBOSE,"51: data incorrect or proccessing not needed");
+ return false;
+ }
+ LDUMP(L_SYS_VERBOSE,buffG,8,"51: cryptokey:");
+ Crypto51Nano(CW,buffG,(PermData[1]>>6)&3,PermData[0]&0x80);
+ // Final Permutation
+ Permute(buffF,PermData,sp->FP());
+ xxor(CW+0,8,CW+0,buffF); xxor(CW+8,8,CW+8,buffF);
+ LDUMP(L_SYS_VERBOSE,hashDW,8,"51: cryptokey:");
+ Crypto51Nano(CW,hashDW,(PermData[1]>>4)&0x03,PermData[0]&0x40);
+ LDUMP(L_SYS_VERBOSE,CW,16,"51: new encrypted CW:");
+ return true;
+}
+
+void cSystemSeca::Permute(unsigned char *data, const unsigned char *pdata, const unsigned char *P)
+{
+ for(int i=7; i>=0; i--) data[i] = P[i] ? pdata[P[i]] : 0;
+}
+
+void cSystemSeca::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+ if(buffer[0]!=0x84) return; // we only support shared updates
+
+ int msgLen=cParseSeca::CmdLen(buffer);
+ if(msgLen!=0x55 && msgLen!=0x63) {
+ if(++emmLenCnt<=5)
+ PRINTF(L_SYS_EMM,"length of EMM does not match (%02x <> 55/63)%s",msgLen,emmLenCnt==5 ? " ....":"");
+ return;
+ }
+ emmLenCnt=0;
+ unsigned char *emm;
+ msgLen=cParseSeca::Payload(buffer,(const unsigned char **)&emm);
+ int provId=cParseSeca::ProvId(buffer);
+ bool SE=false;
+ const unsigned char *T1=T1_S1, *T2=T2_S1;
+ int decrLen=msgLen;
+ if(emm[0]==0x10) { // de-SSE
+ const int rsaKeynr = emm[1]+'0';
+ cPlainKey *pk;
+ cBN exp, mod;
+
+ if(!(pk=keys.FindKey('S',provId,MBC3('E',rsaKeynr,EMM_MAGIC),-1))) return;
+ pk->Get(exp);
+ if(!(pk=keys.FindKey('S',provId,MBC3('M',rsaKeynr,EMM_MAGIC),-1))) return;
+ pk->Get(mod);
+
+ if(rsa.RSA(emm+2,emm+2,msgLen-2,exp,mod)!=msgLen-2) return;
+ const int keyNr=cParseSeca::KeyNr(buffer);
+ if(keyNr&0x80) {
+ SE=true;
+ decrLen-=emm[msgLen-1];
+ if(!GetSeca2Prov(provId,spL) ||
+ !(T1=spL->T1(KEY2INDEX(keyNr))) ||
+ !(T2=spL->T2(KEY2INDEX(keyNr))) ||
+ decrLen<8) return;
+ }
+ }
+
+ for(cSecaCardInfo *ci=Scards.First(); ci; ci=Scards.Next(ci)) {
+ if(ci->MatchEMM(buffer) || (CheckNull(ci->sa,sizeof(ci->sa)) && ci->MatchID(buffer))) {
+ unsigned char MK[16];
+ if(cParseSeca::SysMode(buffer)&0x10) {
+ if(ci->KeySize()!=16) continue; // don't bother
+ memcpy(&MK[0],ci->key,16);
+ }
+ else {
+ memcpy(&MK[0],ci->key,8);
+ memcpy(&MK[8],ci->key,8);
+ }
+
+ unsigned char buff[msgLen], signature[20];
+ memcpy(buff,emm,sizeof(buff)); // if decoding fails we need the original de-sse'd data
+
+ if(!SE) {
+ for(int i=0 ; i<=64 && i<msgLen-8; i+=8) Decrypt(&buff[i],MK,T1,T2);
+ CalcSignature(buff,decrLen-8,signature,MK,T1,T2);
+ }
+ else {
+ spL->CalcSHASignature(buff,decrLen-8,signature);
+ Encrypt(signature,MK,T1,T2);
+ if(memcmp(signature,&buff[decrLen-8],8) ||
+ !spL->DecryptSE(buff+2,MK,msgLen-2,0,T1,T2)) continue;
+ }
+
+ unsigned char keyN[4], *key[4];
+ int pos, len;
+ unsigned int numKeys=0;
+ bool sigOk=false;
+ for(pos=0 ; pos<decrLen ; pos+=len) {
+ int cmd=buff[pos++];
+ len=(cmd>>4)&0x0f;
+ switch(len) {
+ case 0x0b: len=0x0e; break;
+ case 0x0d: len=0x10; break;
+ case 0x0e: len=0x18; break;
+ case 0x0f: len=0x20; break;
+ }
+ switch(cmd) {
+ case 0x82: // signature
+ if(!memcmp(signature,&buff[pos],8)) sigOk=true;
+ pos=decrLen;
+ break;
+ case 0x90: // new primary key
+ if(numKeys<sizeof(keyN)) {
+ keyN[numKeys]=buff[pos] & 0x0f;
+ key[numKeys]=&buff[pos+1];
+ numKeys++;
+ }
+ break;
+ }
+ }
+ char str[20],str2[20],str3[20];
+ if(sigOk) {
+ LBSTARTF(L_SYS_EMM);
+ LBPUT("ID %s SA %s",HexStr(str,SID(buffer),2),HexStr(str2,SA(buffer),3));
+ LBPUT(" - OK (CI ID %s SA %s KEY01 %s)",HexStr(str,ci->provId,2),HexStr(str2,ci->sa,3),HexStr(str3,ci->key,8));
+ for(unsigned int i=0 ; i<numKeys ; i++) {
+ Decrypt(key[i],MK,T1,T2);
+ LBPUT(" KEY %02x %s",keyN[i],HexStr(str,key[i],8));
+ FoundKey();
+ if(keys.NewKey('S',provId,keyN[i],key[i],8)) NewKey();
+ }
+ LBEND();
+ cLoaders::SaveCache();
+ break;
+ }
+ else if(!CheckNull(ci->sa,sizeof(ci->sa)))
+ PRINTF(L_SYS_EMM,"ID %s SA %s - FAIL",HexStr(str,SID(buffer),2),HexStr(str2,SA(buffer),3));
+ }
+ }
+ return;
+}
+
+// -- cSystemLinkSeca ----------------------------------------------------------
+
+class cSystemLinkSeca : public cSystemLink {
+public:
+ cSystemLinkSeca(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemSeca; }
+ virtual bool Init(const char *cfgdir);
+ };
+
+static cSystemLinkSeca staticInit;
+
+cSystemLinkSeca::cSystemLinkSeca(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkSeca::CanHandle(unsigned short SysId)
+{
+ SysId&=SYSTEM_MASK;
+ return SYSTEM_CAN_HANDLE(SysId);
+}
+
+bool cSystemLinkSeca::Init(const char *cfgdir)
+{
+ Scards.Load(cfgdir,SYSTEM_NAME,"Seca.KID");
+ return true;
+}
--- /dev/null
+#
+# Seca
+#
+TARGET = seca
+OBJS = seca.o
+LIBS = -lcrypto
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <malloc.h>
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <linux/dvb/dmx.h>
+
+#include <openssl/des.h>
+
+#include "system-common.h"
+#include "data.h"
+#include "filter.h"
+#include "misc.h"
+#include "log-sys.h"
+
+#define SYSTEM_SHL 0x4A60
+
+#define SYSTEM_NAME "@SHL"
+#define SYSTEM_PRI -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_SHL)
+
+#define L_SYS 15
+#define L_SYS_HOPPER LCLASS(L_SYS,L_SYS_LASTDEF<<1)
+#define L_SYS_HSTATS LCLASS(L_SYS,L_SYS_LASTDEF<<2)
+#define L_SYS_ALL LALL(L_SYS_HSTATS)
+
+static const struct LogModule lm_sys = {
+ (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SYS_DEFDEF)&LOPT_MASK,
+ "shl",
+ { L_SYS_DEFNAMES,"pidHopper","hopperStats" }
+ };
+ADD_MODULE(L_SYS,lm_sys)
+
+// evil hack, but it's not worth to put much effort in a dead system ...
+static cTimeMs __time;
+#define time_ms() ((int)__time.Elapsed())
+
+static cPlainKeyTypeReg<cPlainKeyStd,'Z',false> KeyReg;
+
+// -- cPesFilter ---------------------------------------------------------------
+
+class cPesFilter : public cPidFilter {
+public:
+ cPesFilter(const char *Id, int Num, int DvbNum, int IdleTime);
+ virtual void Start(int Pid, int Section, int Mask, int Mode, bool Crc);
+ };
+
+cPesFilter::cPesFilter(const char *Id, int Num, int DvbNum, int IdleTime)
+:cPidFilter(Id,Num,DvbNum,IdleTime)
+{}
+
+void cPesFilter::Start(int Pid, int Section, int Mask, int Mode, bool Crc)
+{
+ if(fd>=0) {
+ Stop();
+ struct dmx_pes_filter_params FilterParams;
+ FilterParams.input=DMX_IN_FRONTEND;
+ FilterParams.output=DMX_OUT_TAP;
+ FilterParams.pes_type=DMX_PES_OTHER;
+ FilterParams.flags=DMX_IMMEDIATE_START;
+ FilterParams.pid=Pid;
+ CHECK(ioctl(fd,DMX_SET_PES_FILTER,&FilterParams));
+ pid=Pid;
+ active=true;
+ }
+}
+
+// -- cPidHopper ---------------------------------------------------------------
+
+struct FilterPacket {
+ struct FilterPacket *next;
+ int lastTime, dataLen;
+ unsigned char lastData[1];
+ };
+
+struct FilterData {
+ cPidFilter *filter;
+ int lastTime, lastRead, createTime, readCount;
+ struct FilterPacket *first, *last;
+ };
+
+class cPidHopper : public cAction {
+private:
+ struct FilterData *d;
+ int numFilters;
+ cMutex sleepMutex;
+ cCondVar sleep;
+ //
+ void PurgeFilter(struct FilterData *dd);
+ void PurgePacket(struct FilterData *dd, struct FilterPacket *p);
+protected:
+ virtual void Process(cPidFilter *filter, unsigned char *data, int len);
+ virtual cPidFilter *CreateFilter(const char *Id, int Num, int DvbNum, int IdleTime);
+public:
+ cPidHopper(int DvbNum, int NumFilters);
+ virtual ~cPidHopper();
+ int Read(int pid, int & now, unsigned char *buff, int len, int timeout);
+ bool AddPid(int pid);
+ void CutOff(int now);
+ };
+
+cPidHopper::cPidHopper(int DvbNum, int NumFilters)
+:cAction("pidhopper",DvbNum)
+{
+ numFilters=NumFilters;
+ d=MALLOC(struct FilterData,NumFilters);
+ memset(d,0,sizeof(struct FilterData)*NumFilters);
+ PRINTF(L_SYS_HOPPER,"pid hopper started (%d filters)",numFilters);
+}
+
+cPidHopper::~cPidHopper()
+{
+ Lock();
+ int now=time_ms(), num=1;
+ PRINTF(L_SYS_HSTATS,"active pids: (now=%d)\n",now);
+ for(int i=0; i<numFilters; i++) {
+ struct FilterData *dd=&d[i];
+ if(dd->filter) {
+ if(dd->filter->Pid()>=0)
+ PRINTF(L_SYS_HSTATS,"%2d. pid %04x lastTime=%4d lastRead=%4d created=%4d count=%4d",
+ num++,
+ dd->filter->Pid(),
+ dd->lastTime>0 ? (now-dd->lastTime)/1000 : -1,
+ dd->lastRead>0 ? (now-dd->lastRead)/1000 : -1,
+ dd->createTime>0 ? (now-dd->createTime)/1000 : -1,
+ dd->readCount);
+ DelFilter(dd->filter);
+ dd->filter=0;
+ }
+ PurgeFilter(dd);
+ }
+ Unlock();
+ free(d);
+}
+
+void cPidHopper::PurgeFilter(struct FilterData *dd)
+{
+ if(dd->first && dd->last) PurgePacket(dd,dd->last);
+}
+
+void cPidHopper::PurgePacket(struct FilterData *dd, struct FilterPacket *p)
+{
+ while(dd->first) {
+ struct FilterPacket *del=dd->first;
+ dd->first=del->next;
+ if(dd->filter) PRINTF(L_SYS_HOPPER,"PID %04x purging packet %p (time=%d)",dd->filter->Pid(),del,del->lastTime);
+ free(del);
+ if(del==p) break;
+ }
+ if(!dd->first) dd->last=0;
+}
+
+void cPidHopper::CutOff(int now)
+{
+ Lock();
+ PRINTF(L_SYS_HOPPER,"cutoff (now=%d)",now);
+ for(int i=0; i<numFilters; i++) {
+ struct FilterData *dd=&d[i];
+ struct FilterPacket *p=dd->first, *del=0;
+ while(p) {
+ if(p->lastTime>=now) break;
+ del=p;
+ p=p->next;
+ }
+ if(del) {
+ PurgePacket(dd,del);
+ dd->lastTime=dd->first ? dd->first->lastTime : -1;
+ }
+ }
+ Unlock();
+}
+
+cPidFilter *cPidHopper::CreateFilter(const char *Id, int Num, int DvbNum, int IdleTime)
+{
+ cPidFilter *filter=new cPesFilter(Id,Num,DvbNum,IdleTime);
+ d[Num].filter=filter;
+ d[Num].lastTime=-1;
+ d[Num].lastRead=-1;
+ d[Num].createTime=time_ms();
+ d[Num].readCount=0;
+ d[Num].first=0;
+ d[Num].last=0;
+ return filter;
+}
+
+void cPidHopper::Process(cPidFilter *filter, unsigned char *data, int len)
+{
+ if(data && len>0) {
+ int now=time_ms();
+ for(int i=0; i<numFilters; i++)
+ if(d[i].filter==filter) {
+ do {
+ int l=min(len,184);
+ struct FilterPacket *p=(struct FilterPacket *)malloc(sizeof(struct FilterPacket)+l-1);
+ if(p) {
+ p->next=0;
+ memcpy(p->lastData,data,l);
+ p->dataLen=l;
+ p->lastTime=now;
+ if(d[i].first)
+ d[i].last->next=p;
+ else {
+ d[i].first=p;
+ d[i].lastTime=now;
+ }
+ d[i].last=p;
+ PRINTF(L_SYS_HOPPER,"PID %04x queued packet %p (time=%d len=%d) (%s)",d[i].filter->Pid(),p,p->lastTime,l,d[i].first==p?"first":"queue");
+ }
+ len-=l; data+=l;
+ } while(len>0);
+ sleep.Broadcast();
+ break;
+ }
+ }
+}
+
+bool cPidHopper::AddPid(int pid)
+{
+ cPidFilter *filter=NewFilter(0);
+ if(!filter) return false;
+ filter->Start(pid,0,0,0,false);
+ return true;
+}
+
+int cPidHopper::Read(int pid, int & now, unsigned char *buff, int len, int timeout)
+{
+ Lock();
+ timeout+=time_ms();
+ PRINTF(L_SYS_HOPPER,"PID %04x read (now=%d)",pid,now);
+ int n=-1;
+ while(time_ms()<timeout) {
+ struct FilterData *dd=0, *old=0;
+ for(int i=0; i<numFilters; i++)
+ if(d[i].filter) {
+ if(d[i].filter->Pid()==pid) { dd=&d[i]; break; }
+ if(!old ||
+ (d[i].lastRead<0 && d[i].createTime<old->createTime) ||
+ (d[i].lastRead>=0 && d[i].lastRead<old->lastRead))
+ old=&d[i];
+ }
+ if(!dd) {
+ LBSTARTF(L_SYS_VERBOSE);
+ LBPUT("NEW ");
+ if(!AddPid(pid)) {
+ if(!old) break;
+ LBPUT("{0x%04x/%d/%d} ",old->filter->Pid(),old->lastRead>0 ? (time_ms()-old->lastRead)/1000:-1,old->lastTime>0 ? (time_ms()-old->lastTime)/1000:-1);
+ old->filter->Stop(); // purge LRU filter
+ PurgeFilter(old);
+ old->lastTime=-1;
+ old->lastRead=-1;
+ old->createTime=time_ms();
+ old->readCount=0;
+ old->filter->Start(pid,0,0,0,false);
+ }
+ LBEND();
+ continue;
+ }
+ for(struct FilterPacket *p=dd->first; p; p=p->next) {
+ if(p->lastTime>=now) {
+ n=min(len,p->dataLen);
+ memcpy(buff,p->lastData,n);
+ now=p->lastTime;
+ PRINTF(L_SYS_HOPPER,"PID %04x returning packet %p (time=%d)",dd->filter->Pid(),p,p->lastTime);
+ PurgePacket(dd,p);
+ dd->lastRead=time_ms();
+ dd->readCount++;
+ dd->lastTime=dd->first ? dd->first->lastTime : -1;
+ break;
+ }
+ else {
+ PRINTF(L_SYS_HOPPER,"PID %04x skipping packet %p (time=%d)",dd->filter->Pid(),p,p->lastTime);
+ if(!p->next) {
+ PRINTF(L_SYS_HOPPER,"PID %04x queue empty",dd->filter->Pid());
+ PurgePacket(dd,p);
+ break;
+ }
+ }
+ }
+ if(n>0) break;
+ Unlock();
+ sleepMutex.Lock();
+ sleep.TimedWait(sleepMutex,200);
+ sleepMutex.Unlock();
+ Lock();
+ }
+ Unlock();
+ return n;
+}
+
+// -- cShl ---------------------------------------------------------------------
+
+class cShl {
+private:
+ DES_key_schedule sched;
+public:
+ void SetKey( const unsigned char *key);
+ void Decode(unsigned char *data, int n);
+ };
+
+void cShl::SetKey(const unsigned char *key)
+{
+ DES_key_sched((DES_cblock *)key,&sched);
+}
+
+void cShl::Decode(unsigned char *data, int n)
+{
+ int r=n&7;
+ n-=r;
+ for(int i=0; i<n; i+=8)
+ DES_ecb_encrypt((DES_cblock *)&data[i],(DES_cblock *)&data[i],&sched,DES_DECRYPT);
+ if(r) { // possible last incomplete block
+ DES_cblock out;
+ DES_ecb_encrypt((DES_cblock *)&data[n],&out,&sched,DES_DECRYPT);
+ memcpy(&data[n],out,r);
+ }
+}
+
+// -- SHL provider -------------------------------------------------------------
+
+struct ShlProv {
+ const char *name;
+ unsigned short id;
+ bool hasXor;
+ int filters, pidRange;
+ const unsigned short pids[128];
+ const unsigned char xorTab[16];
+ };
+
+static const struct ShlProv prov[] = {
+/*
+ { "FreeX",0x4A90,false,40,0x1300,
+ { 0x1389,0x138a,0x138b,0x138c,0x138d,0x138e,0x138f,0x1390,
+ 0x1391,0x1392,0x1393,0x1394,0x1395,0x1396,0x1397,0x1398,
+ 0x1399,0x139a,0x139b,0x139c,0x13a4,0x13a5,0x13a6,0x13a7,
+ 0x13a8,0x13a9,0x13bf,0x13c0,0x13c1,0x13c2,0x13c3,0x13c4,
+ 0x13c5,0x13c6,0x13c7,0x13c8,0x13c9,0x13ca,0x13cb,0x13cc },
+ {} },
+*/
+ { "FullX-2",0x4A90,false,128,0x0000,
+ { 0x0bb9,0x0bba,0x0bbc,0x0bbf,0x0bc0,0x0bc2,0x0bc3,0x0bc4,
+ 0x0bc7,0x0bc8,0x0bc9,0x0bcc,0x0bcd,0x0bce,0x0bcf,0x0bd0,
+ 0x0bd3,0x0bd5,0x0bd6,0x0bd7,0x0bd8,0x0bd9,0x0bdb,0x0bdd,
+ 0x0bdf,0x0be0,0x0be1,0x0be2,0x0be3,0x0be4,0x0be5,0x0be6,
+ 0x0be7,0x0be8,0x0be9,0x0bea,0x1784,0x177c,0x178a,0x1796,
+ 0x1774,0x1772,0x17a2,0x1783,0x1792,0x177b,0x1775,0x179b,
+ 0x1794,0x1789,0x1778,0x1799,0x1793,0x1797,0x1782,0x1779,
+ 0x179e,0x1787,0x178d,0x177d,0x1771,0x1791,0x1776,0x1788,
+ 0x178f,0x179c,0x177e,0x178b,0x1785,0x1781,0x0bc5,0x17a1,
+ 0x0bbe,0x179f,0x0bdc,0x1780,0x0bca,0x0bd2,0x0bbd,0x179a,
+ 0x1773,0x178e,0x1795 },
+ {} },
+ { "FullX",0x6997,true,60,0x1700,
+ { 0x178e,0x178f,0x1783,0x1780,0x1776,0x177b,0x1772,0x1779,
+ 0x1785,0x179e,0x1791,0x1771,0x177e,0x1793,0x1778,0x179a,
+ 0x17a2,0x17a1,0x178d,0x1787,0x1797,0x1796,0x177d,0x1781,
+ 0x1799,0x1794,0x179b,0x1784,0x1795,0x177c,0x1782,0x179c,
+ 0x1773,0x1788,0x1789,0x178b,0x179f,0x1775,0x1792,0x1774,
+ 0x178a },
+ { 0x4E,0x9E,0x5C,0xFA,0x62,0x80,0x4C,0x86,0x56,0xDF,0x7E,0x03,0x9B,0x05,0xB2,0xE7 } },
+ { "DP",0x6996,true,48,0x1300,
+ { 0x1389,0x138a,0x138b,0x138c,0x138d,0x138e,0x138f,0x1390,
+ 0x1391,0x1392,0x1393,0x1394,0x1395,0x1396,0x1397,0x1398,
+ 0x1399,0x139a,0x139b,0x139c,0x13a4,0x13a5,0x13a6,0x13a7,
+ 0x13a8,0x13a9,0x13bf,0x13c0,0x13c1,0x13c2,0x13c3,0x13c4,
+ 0x13c5,0x13c6,0x13c7,0x13c8,0x13c9,0x13ca,0x13cb,0x13cc },
+ { 0x30,0x61,0xD7,0xC0,0x8E,0x41,0x2C,0xB9,0xAA,0x50,0xB2,0xF4,0x5B,0x2E,0x84,0xCF } }
+ };
+
+static const struct ShlProv *FindProv(unsigned short id)
+{
+ for(unsigned int i=0; i<(sizeof(prov)/sizeof(struct ShlProv)); i++)
+ if(prov[i].id==id) return &prov[i];
+ return 0;
+}
+
+// -- cSystemShl ---------------------------------------------------------------
+
+#define ENC_OFF 12 // encrypted bytes offset
+#define ENC_LEN 24 // encrypted bytes length
+
+#define KEY_OFF 7
+#define PID_OFF 15
+#define EMASK_OFF 8
+#define OMASK_OFF 12
+#define CW_OFF 17
+
+class cSystemShl : public cSystem, private cShl {
+private:
+ cPidHopper *ph;
+ int prvId;
+ bool hasPrvId;
+ cMutex mutex;
+ cCondVar wait;
+ //
+ void ProcessCw(const unsigned char *data, int prvId, const unsigned char *xorTable);
+public:
+ cSystemShl(void);
+ virtual ~cSystemShl();
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *source);
+ virtual void ProcessEMM(int pid, int caid, unsigned char *buffer);
+ };
+
+cSystemShl::cSystemShl(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+ ph=0; hasPrvId=false;
+ hasLogger=needsLogger=true; maxEcmTry=20;
+}
+
+cSystemShl::~cSystemShl()
+{
+ delete ph;
+}
+
+bool cSystemShl::ProcessECM(const cEcmInfo *ecm, unsigned char *source)
+{
+ static const unsigned char tester[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
+ // in old SHL algo ECM starts with 12 0xFF
+ if(SCT_LEN(source)<(ENC_OFF+ENC_LEN+3)) {
+ PRINTF(L_SYS_VERBOSE,"short ecm");
+ return false;
+ }
+ if(memcmp(tester,&source[3],sizeof(tester))) { // new SHL
+ mutex.Lock();
+ if(!hasPrvId) {
+ wait.TimedWait(mutex,500); // wait for provider ID
+ if(!hasPrvId) {
+ PRINTF(L_SYS_ECM,"no provider ID (logger enabled?)");
+ mutex.Unlock();
+ return false;
+ }
+ }
+ const struct ShlProv *prv=FindProv(prvId);
+ if(!prv) {
+ PRINTF(L_SYS_ECM,"provider %04x not supported",prvId);
+ mutex.Unlock();
+ return false;
+ }
+ mutex.Unlock();
+ if(!ph) {
+ PRINTF(L_SYS_ECM,"using card %d",CardNum());
+ ph=new cPidHopper(CardNum(),prv->filters);
+ if(!ph) {
+ PRINTF(L_SYS_ECM,"no pid hopper");
+ return false;
+ }
+#if 1
+ unsigned int l=min((int)(sizeof(prv->pids)/sizeof(unsigned short)),prv->filters);
+ for(unsigned int i=0; i<l; i++) {
+ if(prv->pids[i]<=0) break;
+ ph->AddPid(prv->pids[i]);
+ }
+#endif
+ }
+
+ int pid=ecm->ecm_pid;
+ int now=time_ms();
+ unsigned char buff[4096];
+ unsigned char emask=0, omask=0;
+ memcpy(buff+1,source,SCT_LEN(source)); buff[0]=0;
+ LBSTARTF(L_SYS_VERBOSE);
+ LBPUT("chain 0x%04x [%d] ",pid,SCT_LEN(source)+1);
+ while(1) {
+ unsigned char *ptr=buff;
+ if(ptr[0]!=0) { // decrypt whole payload
+ LBPUT("PES");
+ unsigned char test[8];
+ memcpy(test,ptr,sizeof(test)); Decode(test,sizeof(test));
+ if(test[0]!=source[0] && (ptr[0]==0x11 || ptr[0]==0x12)) { // Section is available at section_pointer offset.
+ SetKey(&ptr[KEY_OFF+1]); // Use the same key and pid offsets which you use
+ Decode(&ptr[PID_OFF+1],183-PID_OFF); // for normal SHL section data decoding.
+ ptr+=ptr[0]+1;
+ if(ptr[0]==0x91 && SCT_LEN(ptr)==11) ptr+=11;
+ LBPUT("s");
+ }
+ else if(test[0]!=source[0] && ptr[0]==0x0a) {
+ SetKey(&ptr[1]);
+ Decode(&ptr[9],183-9);
+ ptr+=ptr[0]+1;
+ LBPUT("a");
+ }
+ else {
+ Decode(ptr,184);
+ LBPUT("n");
+ }
+ LBPUT(" ");
+ }
+ else { // decrypt data only
+ LBPUT("SCT ");
+ ptr++;
+ SetKey(&ptr[KEY_OFF]);
+ Decode(&ptr[PID_OFF],SCT_LEN(ptr)-PID_OFF);
+ }
+ if(ptr[0]!=source[0]) {
+ LBPUT("wrong section %02x != %02x",ptr[0],source[0]);
+ return false;
+ }
+ pid=(ptr[PID_OFF]<<8)+ptr[PID_OFF+1];
+ if(pid==0x1FFF) { // finished
+ if(prv->hasXor) ProcessCw(ptr,prv->id,prv->xorTab);
+ else {
+ for(int i=0; i<8; i++) {
+ const int m=1<<(7-i);
+ if(!(emask & m)) cw[i ]=ptr[CW_OFF+i];
+ if(!(omask & m)) cw[i+8]=ptr[CW_OFF+i+8];
+ }
+ }
+ LBPUT("done");
+ ph->CutOff(now);
+ return true;
+ }
+ else { // next hop
+ if(prv->hasXor) ProcessCw(ptr,prv->id,prv->xorTab);
+ else {
+ emask=ptr[EMASK_OFF];
+ omask=ptr[OMASK_OFF];
+ memcpy(cw,&ptr[CW_OFF],16);
+ }
+ LBPUT("0x%04x ",pid);
+#if 1
+ if(prv->pidRange && (pid&0xFF00)!=prv->pidRange) {
+ LBPUT("wrong pid range");
+ return false;
+ }
+#endif
+ int n=ph->Read(pid,now,buff,sizeof(buff),1000);
+ if(n<CW_OFF+16+1) {
+ LBPUT("read failed");
+ return false;
+ }
+ LBPUT("[%d] ",n);
+ }
+ }
+ LBEND();
+ }
+ else { // old SHL
+ static const unsigned char signature[] = { 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF };
+ source+=ENC_OFF+3; // here's the DES crypto content
+
+ cPlainKey *pk=0;
+ cKeySnoop ks(this,'Z',ecm->provId,0x00);
+ unsigned char key[8];
+ while((pk=keys.FindKey('Z',ecm->provId,0x00,sizeof(key),pk))) {
+ pk->Get(key);
+ SetKey(key);
+ Decode(source,ENC_LEN);
+ unsigned char sigbuf[8];
+ memcpy(&sigbuf[0],&source[0],2);
+ memcpy(&sigbuf[2],&source[18],6);
+ if(!memcmp(sigbuf,signature,sizeof(sigbuf))) {
+ memcpy(cw,&source[2],16);
+ ks.OK(pk);
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+void cSystemShl::ProcessCw(const unsigned char *data, int prvId, const unsigned char *xorTable)
+{
+ if(SCT_LEN(data)<150) {
+ PRINTF(L_SYS_VERBOSE,"data is too short");
+ return;
+ }
+ const unsigned int dataStart=data[33];
+ if((dataStart+35)>184) {
+ PRINTF(L_SYS_VERBOSE,"data broken (%d)",dataStart);
+ return;
+ }
+ const unsigned int xorVal=data[dataStart+35];
+ const unsigned int emask =data[dataStart+34];
+ const unsigned int omask =data[dataStart+33];
+ for(int i=0; i<8; i++) {
+ const unsigned int m=1<<i;
+ if(emask & m) cw[i ]=data[CW_OFF+i ]^xorVal^xorTable[i ];
+ if(omask & m) cw[i+8]=data[CW_OFF+8+i]^xorVal^xorTable[i+8];
+ }
+}
+
+void cSystemShl::ProcessEMM(int pid, int caid, unsigned char *buffer)
+{
+ int n=SCT_LEN(buffer)-3;
+ buffer+=3;
+ for(int i=0; i<n;) {
+ switch(buffer[i]) {
+ case 0x0a:
+ mutex.Lock();
+ prvId=WORD(buffer,i+2,0xFFFF);
+ if(!hasPrvId) {
+ const struct ShlProv *prv=FindProv(prvId);
+ PRINTF(L_SYS_EMM,"provider id %04x (%s)\n",prvId,prv ? prv->name:"unknown");
+ wait.Broadcast();
+ }
+ hasPrvId=true;
+ mutex.Unlock();
+ break;
+ case 0x08:
+ case 0x0b:
+ case 0x09:
+ case 0x06:
+ case 0x02:
+ case 0x30:
+ case 0x07:
+ break;
+ default:
+ HEXDUMP(L_SYS_EMM,&buffer[i+2],buffer[i+1],"unknown nano: %02x:",buffer[i]);
+ break;
+ }
+ i+=buffer[i+1]+2;
+ }
+}
+
+// -- cSystemLinkShl -----------------------------------------------------------
+
+class cSystemLinkShl : public cSystemLink {
+public:
+ cSystemLinkShl(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemShl; }
+ };
+
+static cSystemLinkShl staticInit;
+
+cSystemLinkShl::cSystemLinkShl(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkShl::CanHandle(unsigned short SysId)
+{
+ SysId&=0xFFF0;
+ return SYSTEM_CAN_HANDLE(SysId);
+}
--- /dev/null
+#
+# @SHL
+#
+TARGET = shl
+OBJS = shl.o
+LIBS = -lcrypto
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __LOG_VIACCESS_H
+#define __LOG_VIACCESS_H
+
+#include "log-sys.h"
+
+#define L_SYS 16
+#define L_SYS_TPS LCLASS(L_SYS,L_SYS_LASTDEF<<1)
+#define L_SYS_TPSAU LCLASS(L_SYS,L_SYS_LASTDEF<<2)
+#define L_SYS_TIME LCLASS(L_SYS,L_SYS_LASTDEF<<3)
+#define L_SYS_ST20 LCLASS(L_SYS,L_SYS_LASTDEF<<4)
+#define L_SYS_DISASM LCLASS(L_SYS,L_SYS_LASTDEF<<5)
+#define L_SYS_ALL LALL(L_SYS_DISASM)
+
+#endif
+
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __OPENTV_H
+#define __OPENTV_H
+
+#define COMP_SECTION_HDR 0x434F4D50
+#define INFO_SECTION_HDR 0x494E464F
+#define CODE_SECTION_HDR 0x434F4445
+#define DATA_SECTION_HDR 0x44415441
+#define GDBO_SECTION_HDR 0x4744424F
+#define LAST_SECTION_HDR 0x4C415354
+#define SWAP_SECTION_HDR 0x53574150
+
+typedef struct comp_header {
+ unsigned int magic;
+ unsigned int csize, dsize, usize;
+ unsigned char end;
+} comp_header_t;
+
+typedef struct info_header {
+ unsigned int magic;
+ unsigned int bsssize;
+ unsigned int stacksize;
+} info_header_t;
+
+typedef struct code_header {
+ unsigned int magic;
+ unsigned int size, m_id;
+ unsigned short entry_point;
+ unsigned short end;
+ const unsigned char *code;
+} code_header_t;
+
+typedef struct data_header {
+ unsigned int magic;
+ unsigned int dlen;
+ const unsigned char *data;
+} data_header_t;
+
+#endif /* __OPENTV_H */
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <vdr/tools.h>
+
+#include "log-viaccess.h"
+#include "st20.h"
+
+#include "helper.h"
+#include "misc.h"
+
+//#define SAVE_DEBUG // add some (slow) failsafe checks and stricter emulation
+
+#define INVALID_VALUE 0xCCCCCCCC
+#define ERRORVAL 0xDEADBEEF
+
+#define MININT 0x7FFFFFFF
+#define MOSTPOS 0x7FFFFFFF
+#define MOSTNEG 0x80000000
+
+#ifndef SAVE_DEBUG
+#define POP() stack[(sptr++)&STACKMASK]
+#else
+#define POP() ({ int __t=stack[sptr&STACKMASK]; stack[(sptr++)&STACKMASK]=INVALID_VALUE; __t; })
+#endif
+#define PUSH(v) do { int __v=(v); stack[(--sptr)&STACKMASK]=__v; } while(0)
+#define DROP(n) sptr+=n
+
+#define AAA stack[sptr&STACKMASK]
+#define BBB stack[(sptr+1)&STACKMASK]
+#define CCC stack[(sptr+2)&STACKMASK]
+
+#define GET_OP() operand|=op1&0x0F
+#define CLEAR_OP() operand=0
+#define JUMP(x) Iptr+=(x)
+#define POP64() ({ unsigned int __b=POP(); ((unsigned long long)POP()<<32)|__b; })
+#define PUSHPOP(op,val) do { int __a=val; AAA op##= (__a); } while(0)
+
+#ifndef SAVE_DEBUG
+#define RB(off) UINT8_LE(Addr(off))
+#define RW(off) UINT32_LE(Addr(off))
+#define WW(off,val) BYTE4_LE(Addr(off),val)
+#else
+#define RB(off) ReadByte(off)
+#define RW(off) ReadWord(off)
+#define WW(off,val) WriteWord(off,val)
+#endif
+
+#define LOG_OP(op) do { if(v) LogOp(op); } while(0)
+
+// -- cST20 --------------------------------------------------------------------
+
+cST20::cST20(void)
+{
+ flash=ram=0;
+ loglb=new cLineBuff(128);
+}
+
+cST20::~cST20()
+{
+ free(flash);
+ free(ram);
+ delete loglb;
+}
+
+void cST20::SetFlash(unsigned char *m, int len)
+{
+ free(flash);
+ flash=MALLOC(unsigned char,len);
+ if(flash && m) memcpy(flash,m,len);
+ else memset(flash,0,len);
+ flashSize=len;
+}
+
+void cST20::SetRam(unsigned char *m, int len)
+{
+ free(ram);
+ ram=MALLOC(unsigned char,len);
+ if(ram && m) memcpy(ram,m,len);
+ else memset(ram,0,len);
+ ramSize=len;
+}
+
+void cST20::Init(unsigned int IPtr, unsigned int WPtr)
+{
+ Wptr=WPtr; Iptr=IPtr;
+ memset(stack,INVALID_VALUE,sizeof(stack)); sptr=STACKMAX-3;
+ memset(iram,0,sizeof(iram));
+ verbose=LOG(L_SYS_DISASM);
+}
+
+void cST20::SetCallFrame(unsigned int raddr, int p1, int p2, int p3)
+{
+ Wptr-=16;
+ WriteWord(Wptr,raddr); // RET
+ WriteWord(Wptr+4,0);
+ WriteWord(Wptr+8,0);
+ WriteWord(Wptr+12,p1);
+ WriteWord(Wptr+16,p2);
+ WriteWord(Wptr+20,p3);
+ SetReg(AREG,raddr); // RET
+}
+
+unsigned int cST20::GetReg(int reg) const
+{
+ switch(reg) {
+ case IPTR: return Iptr;
+ case WPTR: return Wptr;
+ case AREG: return AAA;
+ case BREG: return BBB;
+ case CREG: return CCC;
+ default: PRINTF(L_SYS_ST20,"getreg: unknown reg"); return ERRORVAL;
+ }
+}
+
+void cST20::SetReg(int reg, unsigned int val)
+{
+ switch(reg) {
+ case IPTR: Iptr=val; return;
+ case WPTR: Wptr=val; return;
+ case AREG: AAA=val; return;
+ case BREG: BBB=val; return;
+ case CREG: CCC=val; return;
+ default: PRINTF(L_SYS_ST20,"setreg: unknown reg"); return;
+ }
+}
+
+unsigned char *cST20::Addr(unsigned int off)
+{
+ switch(off) {
+ case FLASHS ... FLASHE:
+#ifndef SAVE_DEBUG
+ return &flash[off-FLASHS];
+#else
+ off-=FLASHS; if(off<flashSize && flash) return &flash[off]; break;
+#endif
+ case RAMS ... RAME:
+#ifndef SAVE_DEBUG
+ return &ram[off-RAMS];
+#else
+ off-=RAMS; if(off<ramSize && ram) return &ram[off]; break;
+#endif
+ case IRAMS ... IRAME:
+ return &iram[off-IRAMS];
+ }
+#ifndef SAVE_DEBUG
+ invalid=ERRORVAL; return (unsigned char *)&invalid;
+#else
+ return 0;
+#endif
+}
+
+unsigned int cST20::ReadWord(unsigned int off)
+{
+#ifndef SAVE_DEBUG
+ return UINT32_LE(Addr(off));
+#else
+ unsigned char *addr=Addr(off);
+ return addr ? UINT32_LE(addr) : ERRORVAL;
+#endif
+}
+
+unsigned short cST20::ReadShort(unsigned int off)
+{
+#ifndef SAVE_DEBUG
+ return UINT16_LE(Addr(off));
+#else
+ unsigned char *addr=Addr(off);
+ return addr ? UINT16_LE(addr) : ERRORVAL>>16;
+#endif
+}
+
+unsigned char cST20::ReadByte(unsigned int off)
+{
+#ifndef SAVE_DEBUG
+ return UINT8_LE(Addr(off));
+#else
+ unsigned char *addr=Addr(off);
+ return addr ? UINT8_LE(addr) : ERRORVAL>>24;
+#endif
+}
+
+void cST20::WriteWord(unsigned int off, unsigned int val)
+{
+#ifndef SAVE_DEBUG
+ BYTE4_LE(Addr(off),val);
+#else
+ unsigned char *addr=Addr(off);
+ if(addr) BYTE4_LE(addr,val);
+#endif
+}
+
+void cST20::WriteShort(unsigned int off, unsigned short val)
+{
+#ifndef SAVE_DEBUG
+ BYTE2_LE(Addr(off),val);
+#else
+ unsigned char *addr=Addr(off);
+ if(addr) BYTE2_LE(addr,val);
+#endif
+}
+
+void cST20::WriteByte(unsigned int off, unsigned char val)
+{
+#ifndef SAVE_DEBUG
+ BYTE1_LE(Addr(off),val);
+#else
+ unsigned char *addr=Addr(off);
+ if(addr) BYTE1_LE(addr,val);
+#endif
+}
+
+#define OP_COL 20
+
+void cST20::LogOpOper(int op, int oper)
+{
+ const static char *cmds[] = { "j","ldlp",0,"ldnl","ldc","ldnlp",0,"ldl","adc","call","cj","ajw","eqc","stl","stnl",0 };
+ const static char flags[] = { 3, 0, 0, 0, 1, 0, 0, 0, 1, 3, 3, 0, 1, 0, 0, 0 };
+ if(oper==0) loglb->Printf("%08X %02X",Iptr-1,op);
+ else loglb->Printf("%02X",op);
+ oper|=op&0xF; op>>=4;
+ if(!cmds[op]) return;
+ int n=loglb->Length();
+ if(flags[op]&2) oper+=Iptr;
+ if(flags[op]&1) loglb->Printf("%*s%-5s $%-8X ",max(OP_COL-n,1)," ",cmds[op],oper);
+ else loglb->Printf("%*s%-5s %-8d ",max(OP_COL-n,1)," ",cmds[op],oper);
+}
+
+void cST20::LogOp(char *op)
+{
+ int n=loglb->Length();
+ loglb->Printf("%*s%-15s ",max(OP_COL-n,1)," ",op);
+}
+
+int cST20::Decode(int count)
+{
+ int operand;
+ bool v=verbose;
+ CLEAR_OP();
+ while(Iptr!=0) {
+ int a, op1=RB(Iptr++);
+ if(v) LogOpOper(op1,operand);
+ GET_OP();
+ switch(op1>>4) {
+ case 0x0: // j / jump
+#ifdef SAVE_DEBUG
+ POP(); POP(); POP();
+#endif
+ JUMP(operand);
+ CLEAR_OP();
+ break;
+ case 0x1: // ldlp
+ PUSH(Wptr+(operand*4));
+ CLEAR_OP();
+ break;
+ case 0x2: // positive prefix
+ operand<<=4;
+ break;
+ case 0x3: // ldnl
+ AAA=RW(AAA+(operand*4));
+ CLEAR_OP();
+ break;
+ case 0x4: // ldc
+ PUSH(operand);
+ CLEAR_OP();
+ break;
+ case 0x5: // ldnlp
+ PUSHPOP(+,operand*4);
+ CLEAR_OP();
+ break;
+ case 0x6: // negative prefix
+ operand=(~operand)<<4;
+ break;
+ case 0x7: // ldl
+ PUSH(RW(Wptr+(operand*4)));
+ CLEAR_OP();
+ break;
+ case 0x8: // adc
+ PUSHPOP(+,operand);
+ CLEAR_OP();
+ break;
+ case 0x9: // call
+ Wptr-=16;
+ WW(Wptr,Iptr); WW(Wptr+4,POP()); WW(Wptr+8,POP()); WW(Wptr+12,POP());
+ PUSH(Iptr);
+ JUMP(operand);
+ CLEAR_OP();
+ break;
+ case 0xA: // cj / conditional jump
+ if(AAA) DROP(1); else JUMP(operand);
+ CLEAR_OP();
+ break;
+ case 0xB: // ajw / adjust workspace
+ Wptr+=operand*4;
+ CLEAR_OP();
+ break;
+ case 0xC: // eqc / equals constant
+ AAA=(operand==AAA ? 1 : 0);
+ CLEAR_OP();
+ break;
+ case 0xD: // stl
+ WW(Wptr+(operand*4),POP());
+ CLEAR_OP();
+ break;
+ case 0xE: // stnl
+ a=POP(); WW(a+(operand*4),POP());
+ CLEAR_OP();
+ break;
+ case 0xF: // opr (secondary ins)
+ switch(operand) {
+ case 0x00: LOG_OP("rev"); a=AAA; AAA=BBB; BBB=a; break;
+ case 0x01: LOG_OP("lb"); AAA=RB(AAA); break;
+ case 0x02: LOG_OP("bsub"); PUSHPOP(+,POP()); break;
+ case 0x04: LOG_OP("diff"); PUSHPOP(-,POP()); break;
+ case 0x05: LOG_OP("add"); PUSHPOP(+,POP()); break;
+ case 0x06: LOG_OP("gcall"); a=AAA; AAA=Iptr; Iptr=a; break;
+ case 0x08: LOG_OP("prod"); PUSHPOP(*,POP()); break;
+ case 0x09: LOG_OP("gt"); a=POP(); AAA=(AAA>a); break;
+ case 0x0A: LOG_OP("wsub"); a=POP(); AAA=a+(AAA*4); break;
+ case 0x0C: LOG_OP("sub"); PUSHPOP(-,POP()); break;
+
+ case 0x1B: LOG_OP("ldpi"); PUSHPOP(+,Iptr); break;
+ case 0x20: LOG_OP("ret"); Iptr=RW(Wptr); Wptr=Wptr+16; break;
+ case 0x2C: LOG_OP("div"); PUSHPOP(/,POP()); break;
+ case 0x32: LOG_OP("not"); AAA=~AAA; break;
+ case 0x33: LOG_OP("xor"); PUSHPOP(^,POP()); break;
+ case 0x34: LOG_OP("bcnt"); PUSHPOP(*,4); break;
+ case 0x3B: LOG_OP("sb"); a=POP(); WriteByte(a,POP()); break;
+ case 0x3F: LOG_OP("wcnt"); a=POP(); PUSH(a&3); PUSH((unsigned int)a>>2); break;
+ case 0x40: LOG_OP("shr"); a=POP(); AAA=(unsigned int)AAA>>a; break;
+ case 0x41: LOG_OP("shl"); a=POP(); AAA=(unsigned int)AAA<<a; break;
+ case 0x42: LOG_OP("mint"); PUSH(MOSTNEG); break;
+ case 0x46: LOG_OP("and"); PUSHPOP(&,POP()); break;
+ case 0x4A: LOG_OP("move"); { a=POP(); int b=POP(); int c=POP(); while(a--) WriteByte(b++,ReadByte(c++)); } break;
+ case 0x4B: LOG_OP("or"); PUSHPOP(|,POP()); break;
+ case 0x53: LOG_OP("mul"); PUSHPOP(*,POP()); break;
+ case 0x5A: LOG_OP("dup"); PUSH(AAA); break;
+ case 0x5F: LOG_OP("gtu"); a=POP(); AAA=((unsigned int)AAA>(unsigned int)a); break;
+ case 0x1D: LOG_OP("xdble"); CCC=BBB; BBB=(AAA>=0 ? 0:-1); break;
+ case 0x1A: LOG_OP("ldiv"); { a=POP(); unsigned long long ll=POP64(); PUSH(ll%(unsigned int)a); PUSH(ll/(unsigned int)a); } break;
+ case 0x1F: LOG_OP("rem"); PUSHPOP(%,POP()); break;
+ case 0x35: LOG_OP("lshr"); { a=POP(); unsigned long long ll=POP64()>>a; PUSH((ll>>32)&0xFFFFFFFF); PUSH(ll&0xFFFFFFFF); } break;
+
+ case 0xCA: LOG_OP("ls"); AAA=ReadShort(AAA); break;
+ case 0xCD: LOG_OP("gintdis"); break;
+ case 0xCE: LOG_OP("gintenb"); break;
+
+ case -0x40: LOG_OP("nop"); break;
+
+ default:
+ if(verbose) PUTLB(L_SYS_DISASM,loglb);
+ PRINTF(L_SYS_ST20,"unknown opcode %X",operand);
+ return ERR_ILL_OP;
+ }
+ CLEAR_OP();
+ break;
+ }
+
+ if(v && operand==0) {
+ loglb->Printf("%08X %08X %08X W:%08X",AAA,BBB,CCC,Wptr);
+ PUTLB(L_SYS_DISASM,loglb);
+ }
+ if(--count<=0 && operand==0) {
+ PRINTF(L_SYS_ST20,"instruction counter exceeded");
+ return ERR_CNT;
+ }
+ }
+ return 0;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __ST20_H
+#define __ST20_H
+
+class cLineBuff;
+
+// ----------------------------------------------------------------
+
+#define IPTR 0
+#define WPTR 1
+#define AREG 2
+#define BREG 3
+#define CREG 4
+
+#define FLASHS 0x7FE00000
+#define FLASHE 0x7FFFFFFF
+#define RAMS 0x40000000
+#define RAME 0x401FFFFF
+#define IRAMS 0x80000000
+#define IRAME 0x800017FF
+
+#define ERR_ILL_OP -1
+#define ERR_CNT -2
+
+// ----------------------------------------------------------------
+
+#define STACKMAX 16
+#define STACKMASK (STACKMAX-1)
+
+class cST20 {
+private:
+ unsigned int Iptr, Wptr;
+ unsigned char *flash, *ram;
+ unsigned int flashSize, ramSize;
+ int sptr, stack[STACKMAX];
+ unsigned char iram[0x1800];
+ int invalid;
+ //
+ bool verbose;
+ cLineBuff *loglb;
+ //
+ unsigned char *Addr(unsigned int off);
+ void LogOp(char *op);
+ void LogOpOper(int op, int oper);
+public:
+ cST20(void);
+ ~cST20();
+ void Init(unsigned int IPtr, unsigned int WPtr);
+ void SetCallFrame(unsigned int raddr, int p1, int p2, int p3);
+ void SetFlash(unsigned char *m, int len);
+ void SetRam(unsigned char *m, int len);
+ int Decode(int count);
+ //
+ unsigned int GetReg(int reg) const;
+ void SetReg(int reg, unsigned int val);
+ unsigned int ReadWord(unsigned int off);
+ unsigned short ReadShort(unsigned int off);
+ unsigned char ReadByte(unsigned int off);
+ void WriteWord(unsigned int off, unsigned int val);
+ void WriteShort(unsigned int off, unsigned short val);
+ void WriteByte(unsigned int off, unsigned char val);
+ };
+
+#endif // __ST20_H
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <libsi/section.h>
+
+#include "viaccess.h"
+#include "log-viaccess.h"
+#include "tps.h"
+#include "opentv.h"
+#include "st20.h"
+
+#include "sc.h"
+#include "scsetup.h"
+#include "system-common.h"
+#include "filter.h"
+#include "misc.h"
+#include "helper.h"
+
+#define CHECK_TIME 5*60*1000
+#define LOADBIN_TIME 60*60*1000
+#define TPSAU_TIME 30*60*1000
+
+//#define TRUST_BOXTIME // should we trust the local time on this machine?
+//#define DUMP_TPSAU "/var/tmp"
+
+// -- cRC6 ---------------------------------------------------------------------
+
+/*
+ * This code implements the RC6-32/20 block cipher.
+ *
+ * The algorithm is due to Ron Rivest and RSA Labs. This code is based on code
+ * which was written by Martin Hinner <mhi@penguin.cz> in 1999, no copyright is
+ * claimed.
+ */
+
+#define RC6_WORDSIZE 32
+#define RC6_P32 0xB7E15163L
+#define RC6_Q32 0x9E3779B9L
+
+unsigned int cRC6::rol(unsigned int v, unsigned int cnt)
+{
+ cnt&=(RC6_WORDSIZE-1);
+ return (v<<cnt) | (v>>(RC6_WORDSIZE-cnt));
+}
+
+unsigned int cRC6::ror(unsigned int v, unsigned int cnt)
+{
+ cnt&=(RC6_WORDSIZE-1);
+ return (v>>cnt) | (v<<(RC6_WORDSIZE-cnt));
+}
+
+void cRC6::SetKey(const unsigned char *Key, int len)
+{
+ key[0]=RC6_P32;
+ for(int v=1; v<RC6_MAX; v++) key[v]=key[v-1]+RC6_Q32;
+ len/=4;
+ unsigned int a=0, b=0, l[len];
+ memcpy(l,Key,len*4);
+ for(int i=0,j=0,v=3*(len>RC6_MAX ? len : RC6_MAX) ; v>0; v--) {
+ a=key[i]=rol(key[i]+a+b,3);
+ b= l[j]=rol( l[j]+a+b,a+b);
+ i++; i%=RC6_MAX;
+ j++; j%=len;
+ }
+}
+
+void cRC6::Decrypt(unsigned char *data)
+{
+ unsigned int *l=(unsigned int *)data;
+ unsigned int a, b, c, d;
+ a=l[0]-key[RC6_MAX-2];
+ b=l[1];
+ c=l[2]-key[RC6_MAX-1];
+ d=l[3];
+ for(int i=RC6_ROUNDS; i>0; i--) {
+ unsigned int t=a;
+ unsigned int u=b;
+ a=d; d=c; b=t; c=u;
+ u=rol((d*(2*d+1)),5);
+ t=rol((b*(2*b+1)),5);
+ c=ror(c-key[2*i+1],t)^u;
+ a=ror(a-key[2*i ],u)^t;
+ }
+ l[0]=a;
+ l[1]=b-key[0];
+ l[2]=c;
+ l[3]=d-key[1];
+}
+
+// -- cTransponderTime ---------------------------------------------------------
+
+class cTransponderTime : public cSimpleItem {
+private:
+ time_t sattime;
+ cTimeMs reftime;
+ cMutex mutex;
+ cCondVar wait;
+ int source, transponder;
+ bool hasHandler;
+public:
+ cTransponderTime(int Source, int Transponder);
+ bool Is(int Source, int Transponder) const;
+ void Set(time_t st);
+ time_t Now(void);
+ void SetHasHandler(bool on) { hasHandler=on; }
+ bool HasHandler(void) const { return hasHandler; }
+ };
+
+cTransponderTime::cTransponderTime(int Source, int Transponder)
+{
+ source=Source; transponder=Transponder;
+#ifdef TRUST_BOXTIME
+ sattime=time(0);
+#else
+ sattime=-1;
+#endif
+ hasHandler=false;
+}
+
+bool cTransponderTime::Is(int Source, int Transponder) const
+{
+ return source==Source && transponder==Transponder;
+}
+
+void cTransponderTime::Set(time_t st)
+{
+ cMutexLock lock(&mutex);
+ if(sattime<0 || reftime.Elapsed()>5*1000) {
+ reftime.Set(-100);
+ sattime=st;
+ wait.Broadcast();
+
+ char str[32];
+ struct tm utm;
+ st=Now();
+ time_t now=time(0);
+ gmtime_r(&st,&utm); asctime_r(&utm,str); stripspace(str);
+ PRINTF(L_SYS_TIME,"%x:%x: %s (delta %ld)",source,transponder,str,st-now);
+ }
+}
+
+time_t cTransponderTime::Now(void)
+{
+ cMutexLock lock(&mutex);
+ if(sattime<0) {
+ if(!wait.TimedWait(mutex,3*1000)) {
+ PRINTF(L_SYS_TIME,"%x:%x: unsuccessfull wait",source,transponder);
+ return -1;
+ }
+ }
+ return sattime+(reftime.Elapsed()/1000);
+}
+
+// -- cSatTimeHook -------------------------------------------------------------
+
+class cSatTimeHook : public cLogHook {
+private:
+ cTransponderTime *ttime;
+public:
+ cSatTimeHook(cTransponderTime *Ttime);
+ ~cSatTimeHook();
+ virtual void Process(int pid, unsigned char *data);
+ };
+
+cSatTimeHook::cSatTimeHook(cTransponderTime *Ttime)
+:cLogHook(HOOK_SATTIME,"sattime")
+{
+ ttime=Ttime;
+ pids.AddPid(0x14,0x71,0xff,0x03);
+ ttime->SetHasHandler(true);
+}
+
+cSatTimeHook::~cSatTimeHook()
+{
+ ttime->SetHasHandler(false);
+}
+
+void cSatTimeHook::Process(int pid, unsigned char *data)
+{
+ if(data && ttime) {
+ if(data[0]==0x70) { // TDT
+ SI::TDT tdt(data,false);
+ tdt.CheckParse();
+ ttime->Set(tdt.getTime());
+ }
+ else if(data[0]==0x73) { // TOT
+ SI::TOT tot(data,false);
+ if(!tot.CheckCRCAndParse()) return;
+ ttime->Set(tot.getTime());
+ }
+ }
+}
+
+// -- cSatTime -----------------------------------------------------------------
+
+class cSatTime {
+private:
+ static cMutex mutex;
+ static cSimpleList<cTransponderTime> list;
+ //
+ int cardNum;
+ cTransponderTime *ttime;
+ //
+ void CheckHandler(void);
+public:
+ cSatTime(int CardNum, int Source, int Transponder);
+ time_t Now(void);
+ };
+
+cMutex cSatTime::mutex;
+cSimpleList<cTransponderTime> cSatTime::list;
+
+cSatTime::cSatTime(int CardNum, int Source, int Transponder)
+{
+ cardNum=CardNum;
+ cMutexLock lock(&mutex);
+ for(ttime=list.First(); ttime; ttime=list.Next(ttime))
+ if(ttime->Is(Source,Transponder)) break;
+ if(!ttime) {
+ ttime=new cTransponderTime(Source,Transponder);
+ if(ttime) list.Add(ttime);
+ PRINTF(L_SYS_TIME,"%x:%x: created new transpondertime",Source,Transponder);
+ }
+ else PRINTF(L_SYS_TIME,"%x:%x: using existing transpondertime",Source,Transponder);
+ CheckHandler();
+}
+
+time_t cSatTime::Now(void)
+{
+ CheckHandler();
+ return ttime ? ttime->Now() : -1;
+}
+
+void cSatTime::CheckHandler(void)
+{
+ if(ttime) {
+ if(!cSoftCAM::TriggerHook(cardNum,HOOK_SATTIME) && !ttime->HasHandler()) {
+ cSatTimeHook *hook=new cSatTimeHook(ttime);
+ cSoftCAM::AddHook(cardNum,hook);
+ PRINTF(L_SYS_TIME,"added hook");
+ }
+ }
+}
+
+// -- cOpenTVModule ------------------------------------------------------------
+
+class cOpenTVModule {
+private:
+ int id, modlen;
+ unsigned char *mem;
+ int received;
+ info_header_t info;
+ code_header_t code;
+ data_header_t data;
+ //
+ bool Decompress(void);
+ bool DoDecompress(unsigned char *out_ptr, const unsigned char *in_ptr, int dsize, int data_size, int usize);
+ bool ParseSections(void);
+ void Dump(void);
+public:
+ cOpenTVModule(const unsigned char *data);
+ ~cOpenTVModule();
+ int AddPart(const unsigned char *data, int len);
+ const info_header_t *InfoHdr(void) const { return &info; }
+ const code_header_t *CodeHdr(void) const { return &code; }
+ const data_header_t *DataHdr(void) const { return &data; }
+ //
+ inline static int Id(const unsigned char *data) { return UINT16_BE(&data[3]); }
+ inline static int Offset(const unsigned char *data) { return UINT32_BE(&data[16]); }
+ inline static int Length(const unsigned char *data) { return UINT32_BE(&data[20]); }
+ };
+
+cOpenTVModule::cOpenTVModule(const unsigned char *data)
+{
+ id=Id(data); modlen=Length(data);
+ received=0;
+ mem=MALLOC(unsigned char,modlen);
+}
+
+cOpenTVModule::~cOpenTVModule()
+{
+ free(mem);
+}
+
+int cOpenTVModule::AddPart(const unsigned char *data, int slen)
+{
+ if(Id(data)==id) {
+ int off=Offset(data);
+ int mlen=Length(data);
+ int rec=slen-28;
+ if(mlen!=modlen || (off+rec)>mlen || !mem) {
+ PRINTF(L_SYS_TPSAU,"length mismatch while adding to OpenTV module");
+ return -1;
+ }
+ memcpy(&mem[off],data+24,rec);
+ received+=rec;
+ if(received==mlen) {
+ if(!Decompress()) {
+ PRINTF(L_SYS_TPSAU,"failed to decompress OpenTV module");
+ return -1;
+ }
+ if(!ParseSections()) {
+ PRINTF(L_SYS_TPSAU,"DATA & CODE section not located in OpenTV module");
+ return -1;
+ }
+ Dump();
+ return 1;
+ }
+ }
+ return 0;
+}
+
+void cOpenTVModule::Dump(void)
+{
+#ifdef DUMP_TPSAU
+#warning Dumping TPS AU data
+ {
+ char fname[32];
+ snprintf(fname,sizeof(fname),"%s/decomp.bin.%d.%x",DUMP_TPSAU,getpid(),(int)time(0));
+ int fd=open(fname,O_CREAT|O_TRUNC|O_WRONLY,DEFFILEMODE);
+ if(fd>=0) {
+ write(fd,mem,modlen);
+ close(fd);
+ PRINTF(L_SYS_TPSAU,"dumped to file '%s'",fname);
+ }
+ }
+#endif
+}
+
+bool cOpenTVModule::ParseSections(void)
+{
+ int sections=0;
+ for(int idx=0; idx<modlen;) {
+ unsigned int hdr=UINT32_BE(mem+idx);
+ unsigned int s_len=UINT32_BE(mem+idx+4);
+ switch(hdr) {
+ case INFO_SECTION_HDR:
+ info.magic=hdr;
+ info.bsssize=UINT32_BE(mem+idx+8);
+ info.stacksize=UINT32_BE(mem+idx+12);
+ sections|=1;
+ break;
+ case CODE_SECTION_HDR:
+ code.magic=hdr;
+ code.size=s_len-8;
+ code.m_id=UINT32_BE(mem+idx+8);
+ code.entry_point=UINT16_BE(mem+idx+12);
+ code.end=UINT16_BE(mem+idx+14);
+ code.code=mem+idx+8;
+ sections|=2;
+ break;
+ case DATA_SECTION_HDR:
+ data.magic=hdr;
+ data.dlen=s_len-8;
+ data.data=mem+idx+8;
+ sections|=4;
+ break;
+ case SWAP_SECTION_HDR:
+ case GDBO_SECTION_HDR:
+ break;
+ case LAST_SECTION_HDR:
+ case 0:
+ idx=modlen;
+ break;
+ case COMP_SECTION_HDR:
+ PRINTF(L_GEN_DEBUG,"OpenTV module still compressed in ParseSections()");
+ return 0;
+ default:
+ PRINTF(L_SYS_TPSAU,"unknown section hdr %08x (%c%c%c%c) in OpenTV module",hdr,(hdr>>24)&0xFF,(hdr>>16)&0xFF,(hdr>>8)&0xFF,hdr&0xFF);
+ break;
+ }
+ idx+=s_len;
+ }
+ return sections>=6;
+}
+
+bool cOpenTVModule::Decompress(void)
+{
+ comp_header_t comp;
+ comp.magic=UINT32_BE(mem);
+ comp.csize=UINT32_BE(mem+4);
+ comp.dsize=UINT32_BE(mem+8);
+ comp.usize=UINT32_BE(mem+12);
+ comp.end =UINT8_BE(mem+16);
+ if((COMP_SECTION_HDR!=comp.magic) || (comp.dsize<=comp.csize) ||
+ (comp.usize>comp.dsize) || (comp.end>=1) || (comp.csize<=17))
+ return true;
+ unsigned char *decomp=MALLOC(unsigned char,comp.dsize);
+ if(!decomp || !DoDecompress(decomp,mem+17,comp.dsize,comp.csize-17,comp.usize))
+ return false;
+ free(mem);
+ mem=decomp; modlen=comp.dsize;
+ return true;
+}
+
+#define BYTE() ((odd)?((in_ptr[0]&0x0F)|(in_ptr[1]&0xF0)):in_ptr[0])
+
+#define NIBBLE(__v) \
+ do { \
+ odd^=1; \
+ if(odd) __v=in_ptr[0]&0xF0; \
+ else { __v=(in_ptr[0]&0xF)<<4; in_ptr++; } \
+ } while(0)
+
+bool cOpenTVModule::DoDecompress(unsigned char *out_ptr, const unsigned char *in_ptr, int dsize, int data_size, int usize)
+{
+ if(usize==0) return false;
+ const unsigned char *data_start=in_ptr;
+ unsigned char *out_start=out_ptr;
+ unsigned char *out_end=out_ptr+usize;
+ int odd=0;
+ while(1) {
+ unsigned char mask=BYTE(); in_ptr++;
+ for(int cnt=8; cnt>0; mask<<=1,cnt--) {
+ if(mask&0x80) {
+ out_ptr[0]=BYTE(); in_ptr++;
+ out_ptr++;
+ }
+ else {
+ int off=0, len=0;
+ unsigned char cmd=BYTE(); in_ptr++;
+ switch(cmd>>4) {
+ case 0x0 ... 0x6:
+ off=((cmd&0xF)<<8)+BYTE(); in_ptr++;
+ len=((cmd>>4)&0x7)+3;
+ break;
+ case 0x7:
+ {
+ unsigned char high=BYTE(); in_ptr++;
+ off=((high&0x7F)<<8)+BYTE(); in_ptr++;
+ if((cmd==0x7F) && (high&0x80)) {
+ len=BYTE(); in_ptr++;
+ if(len==0xFF) {
+ len=BYTE(); in_ptr++;
+ len=((len<<8)+BYTE()+0x121)&0xFFFF; in_ptr++;
+ }
+ else len+=0x22;
+ }
+ else {
+ len=((cmd&0x0F)<<1)+3; if(high&0x80) len++;
+ }
+ break;
+ }
+ case 0x8 ... 0xB:
+ if(cmd&0x20) NIBBLE(off); else off=0;
+ off=(off<<1)|(cmd&0x1F); len=2;
+ break;
+ case 0xC:
+ off=cmd&0x0F; len=3;
+ break;
+ case 0xD ... 0xF:
+ NIBBLE(off);
+ off|=cmd&0x0F; len=((cmd>>4)&0x3)+2;
+ break;
+ }
+ const unsigned char *from=out_ptr-(off+1);
+ if(from<out_start || from>=out_ptr || len>(out_end-out_ptr)) {
+ PRINTF(L_SYS_TPSAU,"length mismatch in OpenTV decompress");
+ return false;
+ }
+ while(--len>=0) *out_ptr++=*from++;
+ }
+ if(out_end<=out_ptr) {
+ if(out_end!=out_ptr) {
+ PRINTF(L_SYS_TPSAU,"pointer mismatch in OpenTV decompress");
+ return false;
+ }
+ int len=out_start+dsize-out_ptr;
+ if(len>0) memmove(out_ptr,data_start+(data_size-(dsize-usize)),len);
+ return true;
+ }
+ }
+ }
+}
+
+#undef BYTE
+#undef NIBBLE
+
+// -- cTpsKey ------------------------------------------------------------------
+
+cTpsKey::cTpsKey(void)
+{
+ timestamp=0;
+}
+
+void cTpsKey::Set(const cTpsKey *k)
+{
+ timestamp=k->timestamp;
+ opmode=k->opmode;
+ memcpy(step,k->step,sizeof(step));
+}
+
+void cTpsKey::Set(const unsigned char *mem)
+{
+ timestamp=*((unsigned int *)mem);
+ opmode=mem[52+3];
+ for(int i=0; i<3; i++) {
+ memcpy(step[i].key,&mem[4+i*16],16);
+ step[i].mode=mem[52+i];
+ }
+}
+
+void cTpsKey::Put(unsigned char *mem) const
+{
+ *((unsigned int *)mem)=timestamp;
+ mem[52+3]=opmode;
+ for(int i=0; i<3; i++) {
+ memcpy(&mem[4+i*16],step[i].key,16);
+ mem[52+i]=step[i].mode;
+ }
+}
+
+// -- cTpsAuHook ---------------------------------------------------------------
+
+#define BUFF_SIZE 20000
+
+class cTpsAuHook : public cLogHook {
+private:
+ cOpenTVModule *mod;
+ int pmtpid, aupid;
+public:
+ cTpsAuHook(void);
+ ~cTpsAuHook();
+ virtual void Process(int pid, unsigned char *data);
+ };
+
+// -- cTpsKeys -----------------------------------------------------------------
+
+cTpsKeys tpskeys;
+
+cTpsKeys::cTpsKeys(void)
+:cLoader("TpsAu")
+,lastLoad(-LOADBIN_TIME)
+,lastAu(-TPSAU_TIME)
+{
+ list=new cSimpleList<cTpsKey>;
+ first=last=0; algomem=0;
+}
+
+cTpsKeys::~cTpsKeys()
+{
+ delete list;
+ free(algomem);
+}
+
+const cTpsKey *cTpsKeys::GetKey(time_t t)
+{
+ cMutexLock lock(this);
+ for(cTpsKey *k=list->First(); k; k=list->Next(k))
+ if(t<k->Timestamp()) return k;
+ return 0;
+}
+
+void cTpsKeys::Check(time_t now, int cardnum)
+{
+ checkMutex.Lock();
+ if(first==0 && last==0 && list->Count()>0)
+ GetFirstLast();
+ if(now>0 && lastCheck.Elapsed()>CHECK_TIME) {
+ Purge(now);
+ lastCheck.Set();
+ }
+ bool nokey=now+2*3600>last;
+/*
+ if(lastLoad.Elapsed()>(nokey ? LOADBIN_TIME/60 : LOADBIN_TIME)) {
+ PRINTF(L_SYS_TPSAU,"loading "TPSBIN" triggered");
+ LoadBin();
+ if(now>0) Purge(now);
+ lastLoad.Set();
+ }
+*/
+ if(lastAu.Elapsed()>(nokey ? TPSAU_TIME/60 : TPSAU_TIME)) {
+ if(ScSetup.AutoUpdate>0) {
+ PRINTF(L_SYS_TPSAU,"TPS AU triggered");
+ if(!cSoftCAM::TriggerHook(cardnum,HOOK_TPSAU)) {
+ cTpsAuHook *hook=new cTpsAuHook;
+ cSoftCAM::AddHook(cardnum,hook);
+ PRINTF(L_SYS_TPSAU,"TPS AU hook added");
+ }
+ }
+ lastAu.Set();
+ }
+ checkMutex.Unlock();
+}
+
+void cTpsKeys::Purge(time_t now)
+{
+ cMutexLock lock(this);
+ PRINTF(L_SYS_TPSAU,"purging TPS keylist");
+ bool del=false;
+ for(cTpsKey *k=list->First(); k;) {
+ cTpsKey *n=list->Next(k);
+ if(k->Timestamp()<now-3600) { list->Del(k); del=true; }
+ k=n;
+ }
+ if(del) {
+ GetFirstLast();
+ Modified();
+ cLoaders::SaveCache();
+ }
+}
+
+void cTpsKeys::Join(cSimpleList<cTpsKey> *nlist)
+{
+ cMutexLock lock(this);
+ cTpsKey *k;
+ while((k=nlist->First())) {
+ nlist->Del(k,false);
+ cTpsKey *p=list->First();
+ do {
+ if(!p) {
+ list->Add(k);
+ Modified();
+ break;
+ }
+ cTpsKey *n=list->Next(p);
+ if(k->Timestamp()==p->Timestamp()) {
+ p->Set(k);
+ Modified();
+ delete k;
+ break;
+ }
+ if(k->Timestamp()>p->Timestamp() && (!n || k->Timestamp()<n->Timestamp())) {
+ list->Add(k,p);
+ Modified();
+ break;
+ }
+ p=n;
+ } while(p);
+ }
+ delete nlist;
+ GetFirstLast();
+ cLoaders::SaveCache();
+}
+
+cString cTpsKeys::Time(time_t t)
+{
+ char str[32];
+ struct tm tm_r;
+ strftime(str,sizeof(str),"%b %e %T",localtime_r(&t,&tm_r));
+ return str;
+}
+
+void cTpsKeys::GetFirstLast(void)
+{
+ if(list->Count()>0) {
+ cTpsKey *k=list->First();
+ first=last=k->Timestamp();
+ for(; k; k=list->Next(k)) {
+ if(k->Timestamp()<last)
+ PRINTF(L_SYS_TPSAU,"TPS keys not in accending order!");
+ last=k->Timestamp();
+ }
+ PRINTF(L_SYS_TPS,"%d TPS keys available (from %s to %s)",list->Count(),*Time(first),*Time(last));
+ }
+ else {
+ last=first=0;
+ PRINTF(L_SYS_TPS,"no TPS keys available");
+ }
+}
+
+bool cTpsKeys::ProcessAu(const cOpenTVModule *mod)
+{
+ PRINTF(L_SYS_TPSAU,"processing TPS AU data");
+
+ const code_header_t *codehdr=mod->CodeHdr();
+ const data_header_t *datahdr=mod->DataHdr();
+ const unsigned char *c=codehdr->code;
+ const unsigned char *d=datahdr->data;
+ unsigned int kd=0, cb1=0, cb2=0, cb3=0;
+ for(unsigned int i=0; i<codehdr->size; i++) {
+ if(c[i] == 0x81) { // PushEA DS:$xxxx
+ unsigned int addr=(c[i+1]<<8)|c[i+2];
+ if(addr<(datahdr->dlen-3)) {
+ if(d[addr+1]==0x00 && d[addr+3]==0x00 && d[addr+4]==3) kd=addr;
+ else if(d[addr]==0x73 && d[addr+1]==0x25) {
+ static const unsigned char scan1[] = { 0x28, 0x20, 0x20, 0xC0 };
+ for(int j=2; j < 0xC; j++)
+ if(!memcmp(&d[addr+j],scan1,sizeof(scan1))) { cb1=addr; break; }
+ }
+ else if((d[addr]&0xF0)==0x60 && (d[addr+1]&0xF0)==0xB0) {
+ int vajw = (int)(((~(d[addr]&0x0F))<<4)|(d[addr+1]&0x0F));
+ unsigned char hits=0;
+ for(int j=2; j < 0x30; j++) {
+ int vld = ((d[addr+j]&0x0F)<<4)|(d[addr+j+1]&0x0F);
+ if((d[addr+j]&0xF0)==0x20 && (d[addr+j+1]&0xF0)==0x70) {
+ int val=vajw+vld;
+ if(val==3 || val==4 || val==5) hits++;
+ }
+ }
+ if(hits==3) cb3=addr;
+ else if(hits==2) cb2=addr;
+ }
+ }
+ }
+ }
+ if(!kd || !cb1 || !cb2 || !cb3) {
+ PRINTF(L_SYS_TPSAU,"couldn't locate all pointers in data section");
+ return false;
+ }
+ RegisterAlgo3(d,cb1,cb2,cb3,kd);
+
+ const unsigned char *data=&d[kd];
+ int seclen, numkeys;
+ seclen=data[0] | (data[1]<<8);
+ numkeys=data[2] | (data[3]<<8);
+ int algo=data[4];
+ int mkidx=data[5]&7;
+ unsigned char *sec[7];
+ sec[0]=(unsigned char *)data+6;
+ for(int i=1; i<6; i++) sec[i]=sec[i-1]+seclen;
+ sec[6]=sec[5]+numkeys;
+ unsigned char key[16];
+ cPlainKey *pk=keys.FindKey('V',0x7c00,MBC3('M','K',mkidx),16);
+ if(!pk) {
+ PRINTF(L_SYS_KEY,"missing V 7C00 TPSMK%d key",mkidx);
+ return false;
+ }
+ pk->Get(key);
+
+ if(sec[6]>=d+datahdr->dlen) {
+ PRINTF(L_SYS_TPSAU,"section 5 exceeds buffer");
+ return false;
+ }
+ int keylen=0;
+ for(int i=0; i<numkeys; i++) keylen+=sec[5][i];
+ keylen=(keylen+15)&~15;
+ if(sec[6]+keylen>=d+datahdr->dlen) {
+ PRINTF(L_SYS_TPSAU,"section 6 exceeds buffer");
+ return false;
+ }
+ for(int i=0; i<keylen; i+=16) TpsDecrypt(&sec[6][i],algo,key);
+
+ cSimpleList<cTpsKey> *nlist=new cSimpleList<cTpsKey>;
+ for(int i=0; i<seclen; i++) {
+ static const unsigned char startkey[] = { 0x01,0x01 };
+ static const unsigned char startaes[] = { 0x09,0x10 };
+ static const unsigned char startse[] = { 0x0a,0x10 };
+ unsigned char tmp[56];
+ tmp[0]=sec[0][i];
+ tmp[1]=sec[1][i];
+ tmp[2]=sec[2][i];
+ tmp[3]=sec[3][i];
+ if(CheckFF(tmp,4)) continue;
+ int keyid=sec[4][i];
+ int keylen=sec[5][keyid];
+ if(keylen<32) continue;
+ const unsigned char *tkey=sec[6];
+ for(int j=0; j<keyid; j++) tkey+=sec[5][j];
+
+ unsigned char ke[128];
+ if(keylen!=45) {
+ if(!Handle80008003(tkey,keylen,ke)) continue;
+ tkey=ke;
+ }
+
+ if(memcmp(tkey,startkey,sizeof(startkey))) continue;
+ tmp[52]=0;
+ tmp[53]=tkey[5]; //tkey[4];
+ tmp[54]=1; //tkey[5];
+ tmp[55]=0x1c;
+ tkey+=9;
+ if(memcmp(tkey,startaes,sizeof(startaes))) continue;
+ memset(&tmp[4+ 0],0,16);
+ memcpy(&tmp[4+16],&tkey[2],16);
+ tkey+=18;
+ if(memcmp(tkey,startse,sizeof(startse))) continue;
+ memcpy(&tmp[4+32],&tkey[2],16);
+ cTpsKey *k=new cTpsKey;
+ if(k) { k->Set(tmp); nlist->Add(k); }
+ }
+ PRINTF(L_SYS_TPSAU,"got %d keys from AU data",nlist->Count());
+ bool res=nlist->Count()>0;
+ Join(nlist);
+ return res;
+}
+
+/*
+bool cTpsKeys::LoadBin(void)
+{
+ static const unsigned char mark[] = { 'T','P','S',0 };
+ cFileMap *tpsbin=filemaps.GetFileMap(TPSBIN,FILEMAP_DOMAIN,false);
+ if(!tpsbin) {
+ PRINTF(L_SYS_TPS,"no filemap for "TPSBIN);
+ return false;
+ }
+ else if(!tpsbin->Map()) {
+ PRINTF(L_SYS_TPS,"mapping failed for "TPSBIN);
+ return false;
+ }
+ else if(tpsbin->Size()<65536 || memcmp(mark,tpsbin->Addr()+32,sizeof(mark)) || memcmp(mark,tpsbin->Addr()+48,sizeof(mark))) {
+ PRINTF(L_SYS_TPS,TPSBIN" format not recognised");
+ tpsbin->Unmap();
+ return false;
+ }
+
+ int size=tpsbin->Size()-56;
+ cSimpleList<cTpsKey> *nlist=new cSimpleList<cTpsKey>;
+ for(int i=68; i<size; i+=56) {
+ const unsigned char *a=tpsbin->Addr()+i;
+ if(*((const unsigned int *)a)==0x00000000L || *((const unsigned int *)a)==0xFFFFFFFFL)
+ break;
+ unsigned char tmp[56];
+ DecryptBin(a,tmp);
+ cTpsKey *k=new cTpsKey;
+ if(k) { k->Set(tmp); nlist->Add(k); }
+ }
+ tpsbin->Unmap();
+ PRINTF(L_SYS_TPSAU,"loaded %d keys from "TPSBIN" file",nlist->Count());
+ Join(nlist);
+ return true;
+}
+
+void cTpsKeys::DecryptBin(const unsigned char *in, unsigned char *out)
+{
+ unsigned int var2=*((unsigned int *)in);
+ *((unsigned int *)out)=var2;
+ for(int i=0; i<13; i++) {
+ in+=4; out+=4;
+ var2=(var2<<3) | (var2>>(32-3));
+ unsigned int var1=*((unsigned int *)in) ^ var2;
+ *((unsigned int *)out)=(var1<<(i+2)) | (var1>>(32-(i+2)));
+ }
+}
+*/
+
+bool cTpsKeys::ParseLine(const char *line, bool fromCache)
+{
+ unsigned char tmp[60];
+ if(line[0]=='X') {
+ if(line[1]=='S') {
+ if(algomem) PRINTF(L_SYS_TPS,"multiple start extentions during cache load");
+ if(sscanf(&line[2],"%x %x %x %x",&algolen,&cb1off,&cb2off,&cb3off)==4) {
+ free(algomem);
+ if((algomem=MALLOC(unsigned char,algolen))) {
+ algoread=0;
+ return true;
+ }
+ else PRINTF(L_SYS_TPS,"no memory for algo");
+ }
+ else PRINTF(L_SYS_TPS,"bad format in start extention");
+ }
+ else if(line[1]=='C') {
+ if(algomem) {
+ int off, len;
+ unsigned int crc;
+ if(sscanf(&line[2],"%x %x %n",&off,&crc,&len)==2) {
+ line+=len+2;
+ unsigned char buff[210];
+ if((len=GetHex(line,buff,200,false))) {
+ if(crc==crc32_le(0,buff,len) && off>=0 && off+len<=algolen) {
+ memcpy(&algomem[off],buff,len);
+ algoread+=len;
+ if(algoread==algolen) {
+ RegisterAlgo3(algomem,cb1off,cb2off,cb3off,algolen);
+ free(algomem); algomem=0;
+ }
+ return true;
+ }
+ }
+ }
+ PRINTF(L_SYS_TPS,"bad format in code extention");
+ }
+ else PRINTF(L_SYS_TPS,"unexpected code extention");
+ }
+ else PRINTF(L_SYS_TPS,"unknown extention during cache load");
+ }
+ else {
+ if(GetHex(line,tmp,sizeof(tmp))) {
+ unsigned int crc=crc32_le(0,&tmp[4],sizeof(tmp)-4);
+ if(*((unsigned int *)tmp)==crc) {
+ cTpsKey *k=new cTpsKey;
+ if(k) { k->Set(&tmp[4]); list->Add(k); }
+ return true;
+ }
+ else PRINTF(L_SYS_TPS,"CRC failed during cache load");
+ }
+ }
+ return false;
+}
+
+bool cTpsKeys::Save(FILE *f)
+{
+ cMutexLock lock(this);
+ bool res=true;
+ char str[420];
+ for(cTpsKey *k=list->First(); k; k=list->Next(k)) {
+ unsigned char tmp[60];
+ k->Put(&tmp[4]);
+ *((unsigned int *)tmp)=crc32_le(0,&tmp[4],sizeof(tmp)-4);
+ fprintf(f,"%s\n",HexStr(str,tmp,sizeof(tmp)));
+ res=(ferror(f)==0 && res);
+ }
+ unsigned char *mem;
+ int len=0, cb1=0, cb2=0, cb3=0;
+ if((mem=DumpAlgo3(len,cb1,cb2,cb3))) {
+ fprintf(f,"XS %04X %04X %04X %04X\n",len,cb1,cb2,cb3);
+ res=(ferror(f)==0 && res);
+ for(int i=0; i<len; i+=200) {
+ int l=min(200,len-i);
+ fprintf(f,"XC %04X %08X %s\n",i,crc32_le(0,&mem[i],l),HexStr(str,&mem[i],l));
+ res=(ferror(f)==0 && res);
+ }
+ free(mem);
+ }
+ Modified(!res);
+ return res;
+}
+
+// -- cTpsAuHook ---------------------------------------------------------------
+
+#define AUSID 0x12C0
+
+cTpsAuHook::cTpsAuHook(void)
+:cLogHook(HOOK_TPSAU,"tpsau")
+{
+ mod=0; pmtpid=aupid=-1;
+ pids.AddPid(0x0000,0x00,0xff); // PAT
+}
+
+cTpsAuHook::~cTpsAuHook()
+{
+ delete mod;
+}
+
+void cTpsAuHook::Process(int pid, unsigned char *data)
+{
+ if(data) {
+ if(pid==0) { // PAT
+ SI::PAT pat(data,false);
+ if(pat.CheckCRCAndParse()) {
+ SI::PAT::Association assoc;
+ for(SI::Loop::Iterator it; pat.associationLoop.getNext(assoc,it);) {
+ if(!assoc.isNITPid() && assoc.getServiceId()==AUSID) {
+ pmtpid=assoc.getPid();
+ PRINTF(L_SYS_TPSAU,"got PMT pid %04x for SID %04x",pmtpid,AUSID);
+ cPid *pid=pids.First();
+ if(pid && pid->filter) {
+ pid->filter->Start(pmtpid,0x02,0xFF,0x00,false);
+ pid->filter->Flush();
+ }
+ else PRINTF(L_GEN_DEBUG,"internal: no pid/filter in cTpsAuHook/pat");
+ return;
+ }
+ }
+ PRINTF(L_SYS_TPSAU,"no PMT pid found for SID %04x",AUSID);
+ BailOut();
+ }
+ }
+ else if(pid==pmtpid) { // PMT
+ SI::PMT pmt(data,false);
+ if(pmt.CheckCRCAndParse() && pmt.getServiceId()==AUSID) {
+ SI::PMT::Stream stream;
+ for(SI::Loop::Iterator it; pmt.streamLoop.getNext(stream,it); ) {
+ if(stream.getStreamType()==0x05) {
+ aupid=stream.getPid();
+ PRINTF(L_SYS_TPSAU,"got AU pid %04x",aupid);
+ cPid *pid=pids.First();
+ if(pid && pid->filter) {
+ pid->filter->Start(aupid,0x87,0xFF,0x00,false);
+ pid->filter->Flush();
+ }
+ else PRINTF(L_GEN_DEBUG,"internal: no pid/filter in cTpsAuHook/pmt");
+ return;
+ }
+ }
+ PRINTF(L_SYS_TPSAU,"could not locate AU pid in PMT %04x data",pmtpid);
+ BailOut();
+ }
+ }
+ else if(pid==aupid) {
+ if(cOpenTVModule::Id(data)==2) {
+ if(!mod) mod=new cOpenTVModule(data);
+ if(mod) {
+ int r=mod->AddPart(data,SCT_LEN(data));
+ if(r>0) {
+ PRINTF(L_SYS_TPSAU,"received complete OpenTV module ID 2");
+ if(tpskeys.ProcessAu(mod)) BailOut();
+ r=-1;
+ }
+ if(r<0) { delete mod; mod=0; }
+ }
+ }
+ }
+ }
+}
+
+// -- cTPSDecrypt --------------------------------------------------------------
+
+unsigned char *cTPSDecrypt::mem=0;
+int cTPSDecrypt::memLen=0;
+int cTPSDecrypt::cb1off=0;
+int cTPSDecrypt::cb2off=0;
+int cTPSDecrypt::cb3off=0;
+cMutex cTPSDecrypt::st20Mutex;
+cST20 cTPSDecrypt::st20;
+bool cTPSDecrypt::st20Inited=false;
+
+void cTPSDecrypt::TpsDecrypt(unsigned char *data, short mode, const unsigned char *key)
+{
+ switch(mode) {
+ case 0: break;
+ case 1: cAES::SetKey(key); cAES::Decrypt(data,16); break;
+ case 2: cRC6::SetKey(key,16); cRC6::Decrypt(data); break;
+ case 3: if(mem) {
+ if(!DecryptAlgo3(key,data))
+ PRINTF(L_SYS_TPS,"decrypt failed in algo 3");
+ }
+ else PRINTF(L_SYS_TPS,"no callbacks for algo 3 registered");
+ break;
+ default: PRINTF(L_SYS_TPS,"unknown TPS decryption algo %d",mode); break;
+ }
+}
+
+bool cTPSDecrypt::RegisterAlgo3(const unsigned char *data, int cb1, int cb2, int cb3, int kd)
+{
+ cMutexLock lock(&st20Mutex);
+ free(mem);
+ if(!(mem=MALLOC(unsigned char,kd))) return false;
+ memcpy(mem,data,kd);
+ memLen=kd; cb1off=cb1; cb2off=cb2; cb3off=cb3;
+ st20Inited=false;
+ PRINTF(L_SYS_TPSAU,"registered callbacks for algo 3");
+ return true;
+}
+
+unsigned char *cTPSDecrypt::DumpAlgo3(int &len, int &cb1, int &cb2, int &cb3)
+{
+ cMutexLock lock(&st20Mutex);
+ if(!mem) return 0;
+ unsigned char *buff=MALLOC(unsigned char,memLen);
+ if(!buff) return 0;
+ memcpy(buff,mem,memLen);
+ len=memLen; cb1=cb1off; cb2=cb2off; cb3=cb3off;
+ return buff;
+}
+
+bool cTPSDecrypt::InitST20(void)
+{
+ if(!mem) return false;
+ if(!st20Inited) {
+ st20.SetFlash(mem,memLen);
+ st20.SetRam(NULL,0x10000);
+ st20Inited=true;
+ }
+ return true;
+}
+
+bool cTPSDecrypt::Handle80008003(const unsigned char *src, int len, unsigned char *dest)
+{
+ cMutexLock lock(&st20Mutex);
+ if(cb1off && InitST20()) {
+ for(int i=0; i<len; i++) st20.WriteByte(RAMS+0x400+i,src[i]);
+ st20.WriteShort(RAMS+0x0,0x8000);
+ st20.WriteShort(RAMS+0x2,0x8003);
+ st20.WriteWord(RAMS+0x8,RAMS+0x400);
+ st20.Init(FLASHS+cb1off,RAMS+0xF000);
+ st20.SetCallFrame(0,RAMS,0,0);
+ int err=st20.Decode(1000);
+ if(err<0) {
+ PRINTF(L_SYS_TPSAU,"ST20 processing failed in callback1 (%d)",err);
+ return false;
+ }
+ for(int i=0; i<0x2D; i++) dest[i]=st20.ReadByte(RAMS+0x400+i);
+ return true;
+ }
+ return false;
+}
+
+bool cTPSDecrypt::DecryptAlgo3(const unsigned char *key, unsigned char *data)
+{
+ cMutexLock lock(&st20Mutex);
+ if(cb2off && cb3off && InitST20()) {
+ for(int i=0; i<16; i++) st20.WriteByte(RAMS+0x400+i,key[i]);
+ st20.Init(FLASHS+cb2off,RAMS+0xF000);
+ st20.SetCallFrame(0,RAMS+0x400,RAMS+0x800,0);
+ int err=st20.Decode(20000);
+ if(err<0) {
+ PRINTF(L_SYS_TPS,"ST20 processing failed in callback2 (%d)",err);
+ return false;
+ }
+
+ for(int i=0; i<16; i++) st20.WriteByte(RAMS+0x400+i,data[i]);
+ st20.Init(FLASHS+cb3off,RAMS+0xF000);
+ st20.SetCallFrame(0,RAMS+0x400,RAMS+0x1000,RAMS+0x800);
+ err=st20.Decode(40000);
+ if(err<0) {
+ PRINTF(L_SYS_TPS,"ST20 processing failed in callback3 (%d)",err);
+ return false;
+ }
+ for(int i=0; i<16; i++) data[i]=st20.ReadByte(RAMS+0x1000+i);
+ return true;
+ }
+ return false;
+}
+
+// -- cTPS ---------------------------------------------------------------------
+
+cTPS::cTPS(void)
+{
+ sattime=0;
+}
+
+cTPS::~cTPS()
+{
+ delete sattime;
+}
+
+int cTPS::Decrypt(int cardNum, int Source, int Transponder, unsigned char *data, int len)
+{
+ if(!sattime) {
+ sattime=new cSatTime(cardNum,Source,Transponder);
+ if(!sattime) {
+ PRINTF(L_SYS_TPS,"failed to create time class");
+ return -1;
+ }
+ }
+ time_t now=sattime->Now();
+ tpskeys.Check(now,cardNum);
+ if(now<0) return -1;
+
+ const cTpsKey *k=tpskeys.GetKey(now);
+ if(!k) {
+ PRINTF(L_SYS_TPS,"no TPS key available for current time of day");
+ return -1;
+ }
+ doPost=0;
+ int opmode=k->Opmode(), doTPS=0, doPre=0, hasDF=0, ret=0;
+ if((opmode&4) && data[0]==0xD2 && data[1]==0x01 && data[2]==0x01) {
+ data+=3; len-=3; ret=3;
+ if(data[0]==0x90) PRINTF(L_SYS_TPS,"TPS v1 is no longer supported");
+ if(data[0]==0x40) data[0]=0x90;
+ doTPS=1;
+ }
+ for(int i=0; i<len; i+=data[i+1]+2)
+ switch(data[i]) {
+ case 0xDF:
+ if(!(opmode&4)) doTPS =(0x6996>>((data[i+2]&0xF)^(data[i+2]>>4)))&1;
+ if(opmode&8) doPre =(0x6996>>((data[i+3]&0xF)^(data[i+3]>>4)))&1;
+ if(opmode&16) doPost=(0x6996>>((data[i+4]&0xF)^(data[i+4]>>4)))&1;
+ hasDF=1;
+ break;
+ case 0xEA:
+ if(doPre) TpsDecrypt(&data[i+2],k->Mode(0),k->Key(0));
+ if(doTPS) TpsDecrypt(&data[i+2],(hasDF)?k->Mode(2):1,k->Key(2));
+ break;
+ }
+ postMode=k->Mode(1); memcpy(postKey,k->Key(1),sizeof(postKey));
+ return ret;
+}
+
+void cTPS::PostProc(unsigned char *cw)
+{
+ if(doPost) TpsDecrypt(cw,postMode,postKey);
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __VIACCESS_TPS_H
+#define __VIACCESS_TPS_H
+
+#include <time.h>
+#include <vdr/tools.h>
+#include <vdr/thread.h>
+#include "data.h"
+#include "crypto.h"
+
+class cSatTime;
+class cTpsAuHook;
+class cOpenTVModule;
+class cST20;
+
+// ----------------------------------------------------------------
+
+#define RC6_ROUNDS 20
+#define RC6_MAX (RC6_ROUNDS*2+4)
+
+class cRC6 {
+private:
+ unsigned int key[RC6_MAX];
+ //
+ unsigned int rol(unsigned int v, unsigned int cnt);
+ unsigned int ror(unsigned int v, unsigned int cnt);
+public:
+ void SetKey(const unsigned char *Key, int len);
+ void Decrypt(unsigned char *data);
+ };
+
+// ----------------------------------------------------------------
+
+class cTPSDecrypt : private cAES, private cRC6 {
+private:
+ static unsigned char *mem;
+ static int memLen, cb1off, cb2off, cb3off;
+ static cMutex st20Mutex;
+ static cST20 st20;
+ static bool st20Inited;
+ //
+ static bool InitST20(void);
+ static bool DecryptAlgo3(const unsigned char *key, unsigned char *data);
+protected:
+ void TpsDecrypt(unsigned char *data, short mode, const unsigned char *key);
+ static bool RegisterAlgo3(const unsigned char *data, int cb1, int cb2, int cb3, int kd);
+ static unsigned char *DumpAlgo3(int &len, int &cb1, int &cb2, int &cb3);
+ static bool Handle80008003(const unsigned char *src, int len, unsigned char *dest);
+ };
+
+// ----------------------------------------------------------------
+
+class cTPS : private cTPSDecrypt {
+private:
+ cSatTime *sattime;
+ int doPost;
+ short postMode;
+ unsigned char postKey[16];
+public:
+ cTPS(void);
+ ~cTPS();
+ int Decrypt(int cardNum, int Source, int Transponder, unsigned char *data, int len);
+ void PostProc(unsigned char *cw);
+ };
+
+// ----------------------------------------------------------------
+
+class cTpsKey : public cSimpleItem {
+private:
+ time_t timestamp;
+ int opmode;
+ struct {
+ unsigned char mode;
+ unsigned char key[16];
+ } step[3];
+public:
+ cTpsKey(void);
+ const unsigned char *Key(int st) const { return step[st].key; }
+ int Mode(int st) const { return step[st].mode; }
+ int Opmode(void) const { return opmode; }
+ time_t Timestamp(void) const { return timestamp; }
+ void Set(const cTpsKey *k);
+ void Set(const unsigned char *mem);
+ void Put(unsigned char *mem) const;
+ };
+
+// ----------------------------------------------------------------
+
+class cTpsKeys : public cMutex, public cLoader, private cTPSDecrypt {
+friend class cTpsAuHook;
+private:
+ cSimpleList<cTpsKey> *list;
+ time_t first, last;
+ //
+ cTimeMs lastCheck, lastLoad, lastAu;
+ cMutex checkMutex;
+ //
+ unsigned char *algomem;
+ int algolen, algoread, cb1off, cb2off, cb3off;
+ //
+ void Join(cSimpleList<cTpsKey> *nlist);
+ void Purge(time_t now);
+ void GetFirstLast(void);
+// bool LoadBin(void);
+// void DecryptBin(const unsigned char *in, unsigned char *out);
+ cString Time(time_t t);
+ bool ProcessAu(const cOpenTVModule *mod);
+public:
+ cTpsKeys(void);
+ ~cTpsKeys();
+ const cTpsKey *GetKey(time_t t);
+ void Check(time_t now, int cardnum);
+ virtual bool ParseLine(const char *line, bool fromCache);
+ virtual bool Save(FILE *f);
+ };
+
+extern cTpsKeys tpskeys;
+
+#endif
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "viaccess.h"
+#include "tps.h"
+#include "system-common.h"
+#include "misc.h"
+#include "parse.h"
+#include "log-viaccess.h"
+
+#define SYSTEM_NAME "Viaccess"
+#define SYSTEM_PRI -10
+#define SYSTEM_CAN_HANDLE(x) ((x)==SYSTEM_VIACCESS)
+
+static const struct LogModule lm_sys = {
+ (LMOD_ENABLE|L_SYS_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_SYS_DEFDEF|L_SYS_TPS|L_SYS_TPSAU|L_SYS_TIME|L_SYS_ST20)&LOPT_MASK,
+ "viaccess",
+ { L_SYS_DEFNAMES,"tps","tpsau","time","st20","disasm" }
+ };
+ADD_MODULE(L_SYS,lm_sys)
+
+// -- cPlainKeyVia -------------------------------------------------------------
+
+#define VIA1_KEYLEN 8
+#define VIA2_KEYLEN 16
+#define VIATPS_KEYLEN 16
+
+class cPlainKeyVia : public cPlainKeyStd {
+protected:
+ virtual cString PrintKeyNr(void);
+public:
+ cPlainKeyVia(bool Super);
+ virtual bool Parse(const char *line);
+ virtual bool SetKey(void *Key, int Keylen);
+ virtual bool SetBinKey(unsigned char *Mem, int Keylen);
+ };
+
+static cPlainKeyTypeReg<cPlainKeyVia,'V'> KeyReg;
+
+cPlainKeyVia::cPlainKeyVia(bool Super)
+:cPlainKeyStd(Super)
+{}
+
+bool cPlainKeyVia::SetKey(void *Key, int Keylen)
+{
+ if(keynr==MBC3('T','P','S')) SetSupersede(false);
+ return cPlainKeyStd::SetKey(Key,Keylen);
+}
+
+bool cPlainKeyVia::SetBinKey(unsigned char *Mem, int Keylen)
+{
+ if(keynr==MBC3('T','P','S')) SetSupersede(false);
+ return cPlainKeyStd::SetBinKey(Mem,Keylen);
+}
+
+bool cPlainKeyVia::Parse(const char *line)
+{
+ unsigned char sid[3], skeynr, skey[VIA2_KEYLEN];
+ const char *sline=line;
+ int len;
+ if(GetChar(line,&type,1) && (len=GetHex(line,sid,3,false))) {
+ type=toupper(type); id=Bin2Int(sid,len);
+ line=skipspace(line);
+ bool ok=false;
+ if(!strncasecmp(line,"TPSMK",5) && line[5]>='0' && line[5]<='9') {
+ keynr=MBC3('M','K',line[5]-'0');
+ line+=6;
+ ok=(len=GetHex(line,skey,VIATPS_KEYLEN));
+ }
+ else if(!strncasecmp(line,"TPS ",4)) {
+ line+=4;
+ keynr=MBC3('T','P','S');
+ ok=(len=GetHex(line,skey,VIATPS_KEYLEN));
+ }
+ else if(GetHex(line,&skeynr,1)) {
+ keynr=skeynr;
+ ok=((len=GetHex(line,skey,VIA2_KEYLEN,false)) && (len==VIA1_KEYLEN || len==VIA2_KEYLEN));
+ }
+ if(ok) {
+ SetBinKey(skey,len);
+ return true;
+ }
+ }
+ FormatError("viaccess",sline);
+ return false;
+}
+
+cString cPlainKeyVia::PrintKeyNr(void)
+{
+ return cString::sprintf((keynr==MBC3('T','P','S'))?"TPS":"%02X",keynr);
+}
+
+// -- cViaccessCardInfo --------------------------------------------------------
+
+class cViaccessCardInfo : public cProviderViaccess, public cCardViaccess {
+public:
+ unsigned char keyno, key[8];
+ //
+ bool Parse(const char *line);
+ bool Save(FILE *f) { return true; }
+ bool IsUpdated(void) { return false; }
+ void Updated(void) {}
+ bool Cmp(cViaccessCardInfo *ci) { return false; }
+ };
+
+bool cViaccessCardInfo::Parse(const char *line)
+{
+ return GetHex(line,ident,sizeof(ident)) &&
+ GetHex(line,ua,sizeof(ua)) &&
+ GetHex(line,sa,sizeof(sa)) &&
+ GetHex(line,&keyno,1) &&
+ GetHex(line,key,sizeof(key));
+}
+
+// -- cViaccessCardInfos -------------------------------------------------------
+
+class cViaccessCardInfos : public cCardInfos<cViaccessCardInfo> {
+public:
+ cViaccessCardInfos(void):cCardInfos<cViaccessCardInfo>(SYSTEM_NAME) {}
+ };
+
+static cViaccessCardInfos Vcards;
+
+// -- cViaccess ----------------------------------------------------------------
+
+class cViaccess : protected cDes {
+private:
+ unsigned char v2key[8];
+ bool v2mode;
+ //
+ int HashNanos(const unsigned char *data, int len);
+ void Via2Mod(const unsigned char *key2, unsigned char *data);
+protected:
+ unsigned char hbuff[8], hkey[8];
+ int pH;
+ //
+ void SetV2Mode(const unsigned char *key2);
+ void SetHashKey(const unsigned char *key);
+ void HashByte(unsigned char c);
+ void HashClear(void);
+ void Hash(void);
+ //
+ void Decode(unsigned char *data, const unsigned char *key);
+ bool Decrypt(const unsigned char *work_key, const unsigned char *data, int len, unsigned char *des_data1, unsigned char *des_data2);
+ //
+ virtual unsigned int Mod(unsigned int R, unsigned int key7) const;
+public:
+ cViaccess(void);
+ };
+
+cViaccess::cViaccess(void)
+:cDes()
+{
+ v2mode=false;
+}
+
+/* viaccess DES modification */
+
+unsigned int cViaccess::Mod(unsigned int R, unsigned int key7) const
+{
+ if(key7!=0) {
+ const unsigned int key5=(R>>24)&0xff;
+ unsigned int al=key7*key5 + key7 + key5;
+ al=(al&0xff)-((al>>8)&0xff);
+ if(al&0x100) al++;
+ R=(R&0x00ffffffL) + (al<<24);
+ }
+ return R;
+}
+
+/* viaccess2 modification. Extracted from "Russian wafer" card.
+ A lot of thanks to it's author :) */
+
+void cViaccess::Via2Mod(const unsigned char *key2, unsigned char *data)
+{
+ int kb, db;
+ for(db=7; db>=0; db--) {
+ for(kb=7; kb>3; kb--) {
+ int a0=kb^db;
+ int pos=7;
+ if(a0&4) { a0^=7; pos^=7; }
+ a0=(a0^(kb&3)) + (kb&3);
+ if(!(a0&4)) data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF));
+ }
+ }
+ for(db=0; db<8; db++) {
+ for(kb=0; kb<4; kb++) {
+ int a0=kb^db;
+ int pos=7;
+ if(a0&4) { a0^=7; pos^=7; }
+ a0=(a0^(kb&3)) + (kb&3);
+ if(!(a0&4)) data[db]^=(key2[kb] ^ ((data[kb^pos]*key2[kb^4]) & 0xFF));
+ }
+ }
+}
+
+void cViaccess::Decode(unsigned char *data, const unsigned char *key)
+{
+ if(v2mode) Via2Mod(v2key,data);
+ Des(data,key,VIA_DES);
+ if(v2mode) Via2Mod(v2key,data);
+}
+
+void cViaccess::SetV2Mode(const unsigned char *key2)
+{
+ if(key2) {
+ memcpy(v2key,key2,sizeof(v2key));
+ v2mode=true;
+ }
+ else v2mode=false;
+}
+
+
+void cViaccess::SetHashKey(const unsigned char *key)
+{
+ memcpy(hkey,key,sizeof(hkey));
+}
+
+void cViaccess::HashByte(unsigned char c)
+{
+ hbuff[pH++]^=c;
+ if(pH==8) { pH=0; Hash(); }
+}
+
+void cViaccess::HashClear(void)
+{
+ memset(hbuff,0,sizeof(hbuff));
+ pH=0;
+}
+
+void cViaccess::Hash(void)
+{
+ if(v2mode) Via2Mod(v2key,hbuff);
+ Des(hbuff,hkey,VIA_DES_HASH);
+ if(v2mode) Via2Mod(v2key,hbuff);
+}
+
+int cViaccess::HashNanos(const unsigned char *data, int len)
+{
+ int i=0;
+ pH=0;
+ if(data[0]==0x9f) {
+ HashByte(data[i++]);
+ HashByte(data[i++]);
+ for(int j=0; j<data[1]; j++) HashByte(data[i++]);
+ while(pH!=0) HashByte(0);
+ }
+ for(; i<len; i++) HashByte(data[i]);
+ return i;
+}
+
+bool cViaccess::Decrypt(const unsigned char *work_key, const unsigned char *data, int len, unsigned char *des_data1, unsigned char *des_data2)
+{
+ int pos=0, encStart=0;
+ unsigned char signatur[8];
+ while(pos<len) {
+ switch(data[pos]) {
+ case 0xea: // encrypted bytes
+ encStart = pos + 2;
+ memcpy(des_data1,&data[pos+2],8);
+ memcpy(des_data2,&data[pos+2+8],8);
+ break;
+ case 0xf0: // signature
+ memcpy(signatur,&data[pos+2],8);
+ break;
+ }
+ pos += data[pos+1]+2;
+ }
+ HashClear();
+ SetHashKey(work_key);
+ // key preparation
+ unsigned char prepared_key[8];
+ if(work_key[7]==0) {
+ // 8th key-byte = 0 then like Eurocrypt-M but with viaccess mods
+ HashNanos(data,encStart+16);
+ memcpy(prepared_key,work_key,sizeof(prepared_key));
+ }
+ else { // key8 not zero
+ // rotate the key 2x left
+ prepared_key[0]=work_key[2];
+ prepared_key[1]=work_key[3];
+ prepared_key[2]=work_key[4];
+ prepared_key[3]=work_key[5];
+ prepared_key[4]=work_key[6];
+ prepared_key[5]=work_key[0];
+ prepared_key[6]=work_key[1];
+ prepared_key[7]=work_key[7];
+ // test if key8 odd
+ if(work_key[7]&1) {
+ HashNanos(data,encStart);
+ // test if low nibble zero
+ unsigned char k = ((work_key[7] & 0xf0) == 0) ? 0x5a : 0xa5;
+ for(int i=0; i<8; i++) {
+ unsigned char tmp=des_data1[i];
+ des_data1[i]=(k & hbuff[pH]) ^ tmp;
+ HashByte(tmp);
+ }
+ for(int i=0; i<8; i++) {
+ unsigned char tmp=des_data2[i];
+ des_data2[i]=(k & hbuff[pH]) ^ tmp;
+ HashByte(tmp);
+ }
+ }
+ else {
+ HashNanos(data,encStart+16);
+ }
+ }
+ Decode(des_data1,prepared_key);
+ Decode(des_data2,prepared_key);
+ Hash();
+ return (memcmp(signatur,hbuff,8)==0);
+}
+
+// -- cSystemViaccess ----------------------------------------------------------
+
+#define MAX_NEW_KEYS 5
+
+class cSystemViaccess : public cSystem, private cViaccess {
+private:
+ cTPS tps;
+public:
+ cSystemViaccess(void);
+ virtual bool ProcessECM(const cEcmInfo *ecm, unsigned char *data);
+ virtual void ProcessEMM(int pid, int caid, unsigned char *data);
+ virtual void ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, const unsigned char *data, int len);
+ };
+
+cSystemViaccess::cSystemViaccess(void)
+:cSystem(SYSTEM_NAME,SYSTEM_PRI)
+{
+ hasLogger=true;
+}
+
+void cSystemViaccess::ParseCADescriptor(cSimpleList<cEcmInfo> *ecms, unsigned short sysId, const unsigned char *data, int len)
+{
+ const int pid=WORD(data,2,0x1FFF);
+ if(pid>=0xAA && pid<=0xCF) {
+ PRINTF(L_CORE_ECMPROC,"viaccess: dropped \"fake\" ecm pid 0x%04x",pid);
+ return;
+ }
+ cSystem::ParseCADescriptor(ecms,sysId,data,len);
+}
+
+bool cSystemViaccess::ProcessECM(const cEcmInfo *ecm, unsigned char *data)
+{
+ unsigned char *nanos=(unsigned char *)cParseViaccess::NanoStart(data);
+ int len=SCT_LEN(data)-(nanos-data);
+
+ bool mayHaveTps=false;
+ if(ecm->provId==0x007c00) { // TPS
+ maxEcmTry=4;
+ mayHaveTps=true;
+ int num=tps.Decrypt(CardNum(),ecm->source,ecm->transponder,nanos,len);
+ if(num<0) return false;
+ nanos+=num; len-=num;
+ }
+
+ if(cParseViaccess::CheckNano90FromNano(nanos)) {
+ int keynr=cParseViaccess::KeyNrFromNano(nanos);
+ cKeySnoop ks(this,'V',ecm->provId,keynr);
+ cPlainKey *pk=0;
+ while((pk=keys.FindKey('V',ecm->provId,keynr,-1,pk))) {
+ unsigned char key[16];
+ if(pk->Size()<=(int)sizeof(key)) {
+ pk->Get(key);
+ SetV2Mode(pk->Size()==VIA2_KEYLEN ? &key[VIA1_KEYLEN] : 0);
+ if(cViaccess::Decrypt(key,&nanos[5],len-5,&cw[0],&cw[8])) {
+ if(mayHaveTps) tps.PostProc(cw);
+ ks.OK(pk);
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void cSystemViaccess::ProcessEMM(int pid, int caid, unsigned char *data)
+{
+ for(cViaccessCardInfo *mkey=Vcards.First(); mkey; mkey=Vcards.Next(mkey)) {
+ int updtype;
+ cAssembleData ad(data);
+ if(mkey->cCardViaccess::MatchEMM(data)) {
+ updtype=3;
+ HashClear();
+ memcpy(hbuff+3,mkey->ua,sizeof(mkey->ua));
+ }
+ else if(mkey->cProviderViaccess::MatchEMM(data)) {
+ if(mkey->cProviderViaccess::Assemble(&ad)<0) continue;
+ updtype=2;
+ HashClear();
+ memcpy(hbuff+5,mkey->sa,sizeof(mkey->sa)-1);
+ }
+ else continue;
+
+ const unsigned char *buff;
+ if((buff=ad.Assembled())) {
+ const unsigned char *scan=cParseViaccess::NanoStart(buff);
+ unsigned int scanlen=SCT_LEN(buff)-(scan-buff);
+
+ if(scanlen>=5 && mkey->cProviderViaccess::MatchID(buff) &&
+ cParseViaccess::KeyNrFromNano(scan)==mkey->keyno) {
+ scan+=5; scanlen-=5;
+ SetHashKey(mkey->key);
+ Hash();
+
+ unsigned int n;
+ if(scan[0]==0x9e && scanlen>=(n=scan[1]+2)) {
+ for(unsigned int i=0; i<n; i++) HashByte(scan[i]);
+ Hash(); pH=0;
+ scan+=n; scanlen-=5;
+ }
+ if(scanlen>0) {
+ unsigned char newKey[MAX_NEW_KEYS][8];
+ int numKeys=0, updPrv[MAX_NEW_KEYS]={}, updKey[MAX_NEW_KEYS]={};
+
+ for(unsigned int cnt=0; cnt<scanlen && numKeys<MAX_NEW_KEYS;) {
+ const unsigned int parm=scan[cnt++];
+ unsigned int plen=scan[cnt++];
+
+ switch(parm) {
+ case 0x90:
+ case 0x9E:
+ cnt+=plen;
+ break;
+ case 0xA1: // keyupdate
+ updPrv[numKeys]=(scan[cnt]<<16)+(scan[cnt+1]<<8)+(scan[cnt+2]&0xF0);
+ updKey[numKeys]=scan[cnt+2]&0x0F;
+ // fall through
+ default:
+ HashByte(parm); HashByte(plen);
+ while(plen--) HashByte(scan[cnt++]);
+ break;
+ case 0xEF: // crypted key(s)
+ HashByte(parm); HashByte(plen);
+ if(plen==sizeof(newKey[0])) {
+ const unsigned char k7=mkey->key[7];
+ for(unsigned int kc=0 ; kc<sizeof(newKey[0]) ; kc++) {
+ const unsigned char b=scan[cnt++];
+ if(k7&1) newKey[numKeys][kc]=b^(hbuff[pH]&(k7<0x10 ? 0x5a : 0xa5));
+ else newKey[numKeys][kc]=b;
+ HashByte(b);
+ }
+ numKeys++;
+ }
+ else {
+ PRINTF(L_SYS_EMM,"%d: key length mismatch %d!=%d",CardNum(),plen,sizeof(newKey[0]));
+ cnt=scanlen;
+ }
+ break;
+ case 0xF0: // signature
+ {
+ char str[20], str2[20];
+ static const char *ptext[] = { 0,0,"SHARED","UNIQUE" };
+ const char *addr = (updtype==2) ? HexStr(str,mkey->sa,sizeof(mkey->sa)) : HexStr(str,mkey->ua,sizeof(mkey->ua));
+
+ Hash();
+ if(!memcmp(&scan[cnt],hbuff,sizeof(hbuff))) {
+ unsigned char key[8];
+ memcpy(key,mkey->key,sizeof(key));
+ if(key[7]) { // Rotate key
+ const unsigned char t1=key[0], t2=key[1];
+ key[0]=key[2]; key[1]=key[3]; key[2]=key[4]; key[3]=key[5]; key[4]=key[6];
+ key[5]=t1; key[6]=t2;
+ }
+
+ while(numKeys--) {
+ Decode(newKey[numKeys],key);
+ PRINTF(L_SYS_EMM,"%02X%02X %02X %s %s - KEY %06X.%02X -> %s",
+ mkey->ident[0],mkey->ident[1],mkey->keyno,addr,
+ ptext[updtype],updPrv[numKeys],updKey[numKeys],
+ HexStr(str2,newKey[numKeys],sizeof(newKey[numKeys])));
+ FoundKey();
+ if(keys.NewKey('V',updPrv[numKeys],updKey[numKeys],newKey[numKeys],8)) NewKey();
+ }
+ cLoaders::SaveCache();
+ }
+ else
+ PRINTF(L_SYS_EMM,"%02X%02X %02X %s %s - FAIL",mkey->ident[0],mkey->ident[1],mkey->keyno,addr,ptext[updtype]);
+ cnt=scanlen;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// -- cSystemLinkViaccess ------------------------------------------------------
+
+class cSystemLinkViaccess : public cSystemLink {
+public:
+ cSystemLinkViaccess(void);
+ virtual bool CanHandle(unsigned short SysId);
+ virtual cSystem *Create(void) { return new cSystemViaccess; }
+ virtual bool Init(const char *cfgdir);
+ };
+
+static cSystemLinkViaccess staticInit;
+
+cSystemLinkViaccess::cSystemLinkViaccess(void)
+:cSystemLink(SYSTEM_NAME,SYSTEM_PRI)
+{
+ Feature.NeedsKeyFile();
+}
+
+bool cSystemLinkViaccess::CanHandle(unsigned short SysId)
+{
+ SysId&=SYSTEM_MASK;
+ return SYSTEM_CAN_HANDLE(SysId);
+}
+
+bool cSystemLinkViaccess::Init(const char *cfgdir)
+{
+ Vcards.Load(cfgdir,SYSTEM_NAME,"Viaccess.KID");
+ return true;
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef __VIACCESS_VIACCESS_H
+#define __VIACCESS_VIACCESS_H
+
+#define SYSTEM_VIACCESS 0x0500
+
+#define FILEMAP_DOMAIN "viaccess"
+#define TPSBIN "tps.bin"
+#define HOOK_SATTIME ((SYSTEM_VIACCESS<<8)+0x01)
+#define HOOK_TPSAU ((SYSTEM_VIACCESS<<8)+0x02)
+
+#endif
--- /dev/null
+#
+# Viaccess
+#
+TARGET = viaccess
+OBJS = viaccess.o tps.o st20.o
--- /dev/null
+
+### The directory environment:
+
+VDRDIR = ../../../..
+
+### Allow user defined options to overwrite defaults:
+
+-include $(VDRDIR)/Make.config
+
+### The C++ compiler and options:
+
+CXX ?= g++
+CXXFLAGS ?= -g -march=pentium3 -O2 -Wall -Woverloaded-virtual
+
+### Includes and Defines
+
+SCAPIVERS = $(shell sed -ne '/define SCAPIVERS/ s/^.[a-zA-Z ]*\([0-9]*\).*$$/\1/p' ../sc.c)
+APIVERSION = $(shell sed -ne '/define APIVERSION/ s/^.*"\(.*\)".*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+APIVERSNUM = $(shell sed -ne '/define APIVERSNUM/ s/^.[a-zA-Z ]*\([0-9]*\) .*$$/\1/p' $(VDRDIR)/include/vdr/config.h)
+
+INCLUDES = -I.. -I$(VDRDIR)/include
+DEFINES = -DAPIVERSNUM=$(APIVERSNUM) -DAPIVERSION='"$(APIVERSION)"' -DSCAPIVERS=$(SCAPIVERS) -D_GNU_SOURCE
+
+OBJS = misc.o log.o data.o crypto.o parse.o system.o system-common.o smartcard.o network.o filter.o
+SHAREDOBJS = compat.o $(VDRDIR)/tools.o $(VDRDIR)/thread.o
+LIBS = -lpthread -ljpeg -ldl -lcrypto
+
+NOBJS = $(patsubst %.o,../%.o,$(OBJS))
+
+### Implicit rules:
+
+$(VDRDIR)/%.o: $(VDRDIR)/%.c
+ make -C $(VDRDIR) $(@F)
+
+../%.o: ../%.c
+ make -C .. $(@F)
+
+%.o: %.c
+ $(CXX) $(CXXFLAGS) -c $(DEFINES) $(INCLUDES) $<
+
+### Targets:
+
+all: testECM testEMM testN1Emu testN2Emu
+
+testECM.o: testECM.c compat.h
+testECM: testECM.o $(SHAREDOBJS) $(NOBJS)
+ $(CXX) $(CXXFLAGS) -rdynamic $^ $(LIBS) -o $@
+
+testEMM.o: testEMM.c compat.h
+testEMM: testEMM.o $(SHAREDOBJS) $(NOBJS)
+ $(CXX) $(CXXFLAGS) -rdynamic $^ $(LIBS) -o $@
+
+testN1Emu.o: testN1Emu.c ../systems/nagra/nagra.c ../systems/nagra/nagra1.c ../systems/nagra/cpu.c ../systems/nagra/log-nagra.h
+testN1Emu: testN1Emu.o $(SHAREDOBJS) $(NOBJS)
+ $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@
+
+testN2Emu.o: testN2Emu.c ../systems/nagra/nagra2*.c ../systems/nagra/cpu.c
+testN2Emu: testN2Emu.o $(SHAREDOBJS) $(NOBJS)
+ $(CXX) $(CXXFLAGS) $^ $(LIBS) -o $@
+
+filterhelper: filterhelper.o
+ $(CXX) $(CXXFLAGS) $^ -o $@
+clean:
+ @-rm -f *.o core* *~
+ @-rm -f testECM testEMM testN1Emu testN2Emu
+ @-rm -f filterhelper
--- /dev/null
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <dirent.h>
+#include <fnmatch.h>
+#define DEBUG
+#include "common.h"
+#include "data.h"
+#include "sc.h"
+#include "scsetup.h"
+#include "opts.h"
+#include "misc.h"
+#include "system.h"
+#include "smartcard.h"
+#include "cam.h"
+#include "log.h"
+
+#define LIBSC_PREFIX "libsc-"
+#define SO_INDICATOR ".so."
+
+static bool DllLoad(const char *fileName)
+{
+ const char *base=rindex(fileName,'/');
+ if(!base) base=fileName;
+ void *handle=dlopen(fileName,RTLD_NOW|RTLD_LOCAL);
+ if(handle) return true;
+ printf("dload: %s: %s\n",base,dlerror());
+ return false;
+}
+
+bool DllsLoad(const char *libdir)
+{
+ char pat[32];
+ snprintf(pat,sizeof(pat),"%s*-%d%s%s",LIBSC_PREFIX,SCAPIVERS,SO_INDICATOR,APIVERSION);
+ bool res=true;
+ cReadDir dir(libdir);
+ struct dirent *e;
+ while((e=dir.Next())) {
+ if(!fnmatch(pat,e->d_name,FNM_PATHNAME|FNM_NOESCAPE)) {
+ DllLoad(AddDirectory(libdir,e->d_name));
+ }
+ }
+ return res;
+}
+
+void InitAll(const char *cfgdir)
+{
+ logcfg.logCon=1;
+ cSystems::ConfigParse("Cardclient.Immediate","0");
+
+ filemaps.SetCfgDir(cfgdir);
+ if(!keys.Load(cfgdir)) printf("ERROR: no keys loaded for softcam!\n");
+ if(!cSystems::Init(cfgdir)) exit(2);
+ cLoaders::LoadCache(cfgdir);
+ smartcards.LoadData(cfgdir);
+#ifdef DEFAULT_PORT
+ smartcards.AddPort(DEFAULT_PORT);
+#endif
+ smartcards.LaunchWatcher();
+}
+
+void LogAll(void)
+{
+ for(int i=0; i<32; i++)
+ cLogging::SetModuleOptions(LCLASS(i,0xFFFFFFFF));
+}
+
+void SDump(const unsigned char *buffer, int n)
+{
+ for(int l=0 ; l<n ; l++) printf("%02x ",buffer[l]);
+ printf("\n");
+}
+
+int ReadRaw(const char *name, unsigned char *buff, int maxlen)
+{
+ FILE *f=fopen(name,"r");
+ if(f) {
+ char line[512];
+ int len=0;
+ while(len<maxlen && fgets(line,sizeof(line),f)) {
+ if(strstr(line,"dump: n=")) continue;
+ char *p=index(line,':');
+ if(p) p++; else p=line;
+ while(1) {
+ char *np;
+ int val=strtol(p,&np,16);
+ if(p!=np) {
+ buff[len++]=val;
+ p=np;
+ }
+ else break;
+ }
+ }
+ fclose(f);
+ printf("using raw from %s:\n",name);
+ HexDump(buff,len);
+ return len;
+ }
+ printf("failed to open raw file %s: %s\n",name,strerror(errno));
+ exit(1);
+}
+
+//
+//
+
+extern const char *I18nTranslate(const char *s, const char *Plugin)
+{
+ return s;
+}
+
+//
+//
+
+static const struct LogModule lm_core = {
+ (LMOD_ENABLE|L_CORE_ALL)&LOPT_MASK,
+ (LMOD_ENABLE|L_CORE_LOAD|L_CORE_ECM|L_CORE_PIDS|L_CORE_AU|L_CORE_AUSTATS|L_CORE_CAIDS|L_CORE_NET|L_CORE_CI|L_CORE_SC)&LOPT_MASK,
+ "core",
+ { "load","action","ecm","ecmProc","pids","au","auStats","auExtra","auExtern",
+ "caids","keys","dynamic","csa","ci","av7110","net","netData","msgcache",
+ "serial","smartcard" }
+ };
+ADD_MODULE(L_CORE,lm_core)
+
+//
+//
+
+cScSetup ScSetup;
+
+cScSetup::cScSetup(void)
+{
+ AutoUpdate = 1;
+ memset(ScCaps,0,sizeof(ScCaps));
+ ScCaps[0] = 1;
+ ScCaps[1] = 2;
+ ConcurrentFF = 1;
+ memset(CaIgnore,0,sizeof(CaIgnore));
+}
+
+void cScSetup::Check(void) {}
+bool cScSetup::Ignore(unsigned short caid) { return false; }
+
+//
+//
+
+void cSoftCAM::SetLogStatus(int CardNum, const cEcmInfo *ecm, bool on) {}
+
+//
+//
+
+// --- cOpt --------------------------------------------------------------------
+
+cOpt::cOpt(const char *Name, const char *Title)
+{
+ name=Name; title=Title;
+ fullname=0; persistant=true;
+}
+
+cOpt::~cOpt()
+{
+ free(fullname);
+}
+
+const char *cOpt::FullName(const char *PreStr)
+{
+ if(PreStr) {
+ free(fullname);
+ asprintf(&fullname,"%s.%s",PreStr,name);
+ return fullname;
+ }
+ else return name;
+}
+
+// --- cOptInt -----------------------------------------------------------------
+
+cOptInt::cOptInt(const char *Name, const char *Title, int *Storage, int Min, int Max)
+:cOpt(Name,Title)
+{
+ storage=Storage; min=Min; max=Max;
+}
+
+void cOptInt::Parse(const char *Value)
+{
+ *storage=atoi(Value);
+}
+
+void cOptInt::Backup(void)
+{
+}
+
+bool cOptInt::Set(void)
+{
+ if(value!=*storage) { *storage=value; return true; }
+ return false;
+}
+
+void cOptInt::Store(const char *PreStr)
+{
+}
+
+void cOptInt::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOptSel -----------------------------------------------------------------
+
+cOptSel::cOptSel(const char *Name, const char *Title, int *Storage, int NumStr, const char * const *Strings)
+:cOptInt(Name,Title,Storage,0,NumStr)
+{
+ strings=Strings;
+ trStrings=0;
+}
+
+cOptSel::~cOptSel()
+{
+ free(trStrings);
+}
+
+void cOptSel::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOptBool -----------------------------------------------------------------
+
+cOptBool::cOptBool(const char *Name, const char *Title, int *Storage)
+:cOptInt(Name,Title,Storage,0,1)
+{}
+
+void cOptBool::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOptStr -----------------------------------------------------------------
+
+cOptStr::cOptStr(const char *Name, const char *Title, char *Storage, int Size, const char *Allowed)
+:cOpt(Name,Title)
+{
+ storage=Storage; size=Size; allowed=Allowed;
+ value=MALLOC(char,size);
+}
+
+cOptStr::~cOptStr()
+{
+ free(value);
+}
+
+void cOptStr::Parse(const char *Value)
+{
+ strn0cpy(storage,Value,size);
+}
+
+void cOptStr::Backup(void)
+{
+}
+
+bool cOptStr::Set(void)
+{
+ if(strcmp(value,storage)) { strn0cpy(storage,value,size); return true; }
+ return false;
+}
+
+void cOptStr::Store(const char *PreStr)
+{
+}
+
+void cOptStr::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOptCap -----------------------------------------------------------------
+
+class cOptCap : public cOpt {
+protected:
+ int *storage, *value;
+ int size, disp, len;
+public:
+ cOptCap(const char *Name, const char *Title, int *Storage, int Size, int Disp);
+ virtual ~cOptCap();
+ virtual void Parse(const char *Value);
+ virtual void Backup(void);
+ virtual bool Set(void);
+ virtual void Store(const char *PreStr);
+ virtual void Create(cOsdMenu *menu);
+ };
+
+cOptCap::cOptCap(const char *Name, const char *Title, int *Storage, int Size, int Disp)
+:cOpt(Name,Title)
+{
+ storage=Storage; size=Size; disp=Disp; len=sizeof(int)*size;
+ value=MALLOC(int,size);
+}
+
+cOptCap::~cOptCap()
+{
+ free(value);
+}
+
+void cOptCap::Parse(const char *Value)
+{
+ memset(storage,0,len);
+ int i=0;
+ while(1) {
+ char *p;
+ const int c=strtol(Value,&p,10);
+ if(c<=0 || p==Value || i>=size) return;
+ storage[i++]=c;
+ Value=p;
+ }
+}
+
+void cOptCap::Backup(void)
+{
+}
+
+bool cOptCap::Set(void)
+{
+ if(memcmp(value,storage,len)) { memcpy(storage,value,len); return true; }
+ return false;
+}
+
+void cOptCap::Store(const char *PreStr)
+{
+}
+
+void cOptCap::Create(cOsdMenu *menu)
+{
+}
+
+// --- cOpts -------------------------------------------------------------------
+
+cOpts::cOpts(const char *PreStr, int NumOpts)
+{
+ preStr=PreStr;
+ numOpts=NumOpts; numAdd=0;
+ if((opts=MALLOC(cOpt *,numOpts))) memset(opts,0,sizeof(cOpt *)*numOpts);
+}
+
+cOpts::~cOpts()
+{
+ if(opts) {
+ for(int i=0; i<numOpts; i++) delete opts[i];
+ free(opts);
+ }
+}
+
+void cOpts::Add(cOpt *opt)
+{
+ if(opts && numAdd<numOpts) opts[numAdd++]=opt;
+}
+
+bool cOpts::Parse(const char *Name, const char *Value)
+{
+ if(opts) {
+ for(int i=0; i<numAdd; i++)
+ if(opts[i] && opts[i]->Persistant() && !strcasecmp(Name,opts[i]->Name())) {
+ opts[i]->Parse(Value);
+ return true;
+ }
+ }
+ return false;
+}
+
+bool cOpts::Store(bool AsIs)
+{
+ return false;
+}
+
+void cOpts::Backup(void)
+{
+}
+
+void cOpts::Create(cOsdMenu *menu)
+{
+}
+
+//
+//
+// VDR
+//
+//
+
+#include <vdr/sources.h>
+
+cString cSource::ToString(int Code)
+{
+ char buffer[16];
+ char *q = buffer;
+ switch (Code & st_Mask) {
+ case stCable: *q++ = 'C'; break;
+ case stSat: *q++ = 'S';
+ {
+ int pos = Code & ~st_Mask;
+ q += snprintf(q, sizeof(buffer) - 2, "%u.%u", (pos & ~st_Neg) / 10, (pos & ~st_Neg) % 10); // can't simply use "%g" here since the silly 'locale' messes up the decimal point
+ *q++ = (Code & st_Neg) ? 'E' : 'W';
+ }
+ break;
+ case stTerr: *q++ = 'T'; break;
+ default: *q++ = Code + '0'; // backward compatibility
+ }
+ *q = 0;
+ return buffer;
+}
+
+int cSource::FromString(const char *s)
+{
+ int type = stNone;
+ switch (toupper(*s)) {
+ case 'C': type = stCable; break;
+ case 'S': type = stSat; break;
+ case 'T': type = stTerr; break;
+ case '0' ... '9': type = *s - '0'; break; // backward compatibility
+ default: esyslog("ERROR: unknown source key '%c'", *s);
+ return stNone;
+ }
+ int code = type;
+ if (type == stSat) {
+ int pos = 0;
+ bool dot = false;
+ bool neg = false;
+ while (*++s) {
+ switch (toupper(*s)) {
+ case '0' ... '9': pos *= 10;
+ pos += *s - '0';
+ break;
+ case '.': dot = true;
+ break;
+ case 'E': neg = true; // fall through to 'W'
+ case 'W': if (!dot)
+ pos *= 10;
+ break;
+ default: esyslog("ERROR: unknown source character '%c'", *s);
+ return stNone;
+ }
+ }
+ if (neg)
+ pos |= st_Neg;
+ code |= pos;
+ }
+ return code;
+}
+
+//
+//
+
+#include <vdr/channels.h>
+
+int cChannel::Transponder(int Frequency, char Polarization)
+{
+ // some satellites have transponders at the same frequency, just with different polarization:
+ switch (tolower(Polarization)) {
+ case 'h': Frequency += 100000; break;
+ case 'v': Frequency += 200000; break;
+ case 'l': Frequency += 300000; break;
+ case 'r': Frequency += 400000; break;
+ }
+ return Frequency;
+}
--- /dev/null
+
+bool DllsLoad(const char *libdir);
+void InitAll(const char *cfgdir);
+void LogAll(void);
+void SDump(const unsigned char *buffer, int n);
+int ReadRaw(const char *name, unsigned char *buff, int maxlen);
--- /dev/null
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+
+int main(int argc, char *argv[])
+{
+ unsigned char match[256];
+ int i, max;
+ int filt, mask, mode;
+ int mam, manm;
+
+ if(argc<2) {
+ printf("missing operation mode\n");
+ exit(1);
+ }
+ if(!strcasecmp(argv[1],"MATCH")) {
+ if(argc<3) {
+ printf("no matches given\n");
+ exit(1);
+ }
+ memset(match,0,sizeof(match));
+ printf("searching filter settings for:");
+ for(i=2; i<argc; i++) {
+ int l=strtol(argv[i],0,0);
+ if(l<0 || l>255) {
+ printf("allowed range 0-255 / 0x00-0xFF\n");
+ exit(1);
+ }
+ match[l]=1;
+ printf(" 0x%02x",l);
+ }
+ printf("\n");
+
+ max=5;
+ for(mode=0; mode<256; mode++) {
+ for(mask=0; mask<256; mask++) {
+ mam =mask & (~mode);
+ manm=mask & ~(~mode);
+ for(filt=0; filt<256; filt++) {
+ int ok=1, miss=0;
+ unsigned char mm[256];
+ memset(mm,0,sizeof(mm));
+ for(i=0; i<256; i++) {
+ int xxor=filt^i;
+ if((mam&xxor) || (manm && !(manm&xxor))) {
+ if(match[i]!=0) {
+ ok=0;
+ miss=0;
+ break;
+ }
+ }
+ else {
+ if(match[i]!=1) {
+ ok=0;
+ miss++; mm[i]=1;
+ if(miss>max) break;
+ }
+ }
+ }
+
+ if(ok) {
+ printf("found exact settings filt=%02x mask=%02x mode=%02x\n",filt,mask,mode);
+ exit(0);
+ }
+ else if(miss>0 && miss<=max) {
+ printf("mismatching");
+ for(i=0; i<256; i++) if(mm[i]) printf(" %02x",i);
+ printf(" - settings filt=%02x mask=%02x mode=%02x\n",filt,mask,mode);
+ max=miss;
+ }
+ }
+ }
+ }
+ printf("no exact settings found\n");
+ exit(1);
+ }
+ else if(!strcasecmp(argv[1],"TEST")) {
+ if(argc<5) {
+ printf("missing arguments\n");
+ exit(1);
+ }
+ filt=strtol(argv[2],0,0);
+ if(filt<0 || filt>255) {
+ printf("filter range 0-255 exceeded\n");
+ exit(1);
+ }
+ mask=strtol(argv[3],0,0);
+ if(mask<0 || mask>255) {
+ printf("mask range 0-255 exceeded\n");
+ exit(1);
+ }
+ mode=strtol(argv[4],0,0);
+ if(mode<0 || mode>255) {
+ printf("mode range 0-255 exceeded\n");
+ exit(1);
+ }
+ printf("settings file=%02x mask=%02x mode=%02x\n",filt,mask,mode);
+
+ mam =mask & (~mode);
+ manm=mask & ~(~mode);
+ printf("matching:");
+ for(i=0; i<256; i++) {
+ int xxor=filt^i;
+ if((mam&xxor) || (manm && !(manm&xxor))) {}
+ else
+ printf(" %02x",i);
+ }
+ printf("\n");
+ }
+ else {
+ printf("unknown operation mode\n");
+ }
+ exit(1);
+}
+
--- /dev/null
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "data.h"
+#include "system.h"
+#include "compat.h"
+
+int main(int argc, char *argv[])
+{
+ if(argc<5) {
+ printf("usage: %s <lib-dir> <plugin-dir> <caid> <provid> <ecm-dump>\n",argv[0]);
+ return 1;
+ }
+ DllsLoad(argv[1]);
+ InitAll(argv[2]);
+ unsigned char ecm[4096];
+ ReadRaw(argv[5],ecm,sizeof(ecm));
+
+ int caid=strtol(argv[3],0,0);
+ int provid=strtol(argv[4],0,0);
+ printf("using caid %04x provid %04x\n",caid,provid);
+ cEcmInfo ecmD("dummy",0x123,caid,provid);
+ cSystem *sys=0;
+ int lastPri=0;
+ while((sys=cSystems::FindBySysId(caid,false,lastPri))) {
+ lastPri=sys->Pri();
+ printf("processing with module '%s'\n",sys->Name());
+ bool res=sys->ProcessECM(&ecmD,ecm);
+ delete sys;
+ if(res) break;
+ }
+}
--- /dev/null
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "common.h"
+#include "data.h"
+#include "system.h"
+#include "compat.h"
+
+int main(int argc, char *argv[])
+{
+ if(argc<4) {
+ printf("usage: %s <lib-dir> <plugin-dir> <caid> <rawemm-file>\n",argv[0]);
+ return 1;
+ }
+ DllsLoad(argv[1]);
+ InitAll(argv[2]);
+ LogAll();
+ unsigned char emm[4096];
+ int len=ReadRaw(argv[4],emm,sizeof(emm));
+
+ int caid=strtol(argv[3],0,0);
+ printf("using caid %04x\n",caid);
+ cSystem *sys=0;
+ int lastPri=0;
+ while((sys=cSystems::FindBySysId(caid,false,lastPri))) {
+ sys->DoLog(true); sys->CardNum(0);
+ lastPri=sys->Pri();
+ if(sys->HasLogger()) {
+ for(int i=0; i<len;) {
+ int s=SCT_LEN(&emm[i]);
+ sys->ProcessEMM(0x123,caid,&emm[i]);
+ i+=s;
+ }
+ }
+ delete sys;
+ }
+}
--- /dev/null
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#define TESTER
+#include "crypto.h"
+#include "data.h"
+#include "system-common.h"
+#include "systems/nagra/cpu.c"
+#include "systems/nagra/nagra.c"
+#include "systems/nagra/nagra1.c"
+
+#include "compat.h"
+
+class cTestNagra : public cNagra1 {
+public:
+ virtual void Process(int id, unsigned char *emmdata);
+ };
+
+void cTestNagra::Process(int id, unsigned char *emmdata)
+{
+ cEmu *emu=0;
+ cBN n1, e1;
+
+ unsigned int i=0;
+ bool gotKeys=false;
+ unsigned char key0[8], key1[8];
+ int keyId=(emmdata[i+1]<<8)+emmdata[i+2];
+ switch(emmdata[i]) { // Check filter type
+ case 0x00: // One card
+ i+=7; break;
+ case 0x01 ... 0x20: // Group of cards
+ i+=emmdata[i]+0x7; break;
+ case 0x3e:
+ i+=6; break;
+ case 0x3d: // All cards with same system ID
+ case 0x3f:
+ i+=3; break;
+ }
+ int nrKeys=0;
+ while(i<sizeof(emmdata)) {
+ if((emmdata[i]&0xF0)==0xF0) { // Update with CPU code
+ const int romNr=emmdata[i]&0x0F;
+ if(!emu || !emu->Matches(romNr,id)) {
+ delete emu; emu=0;
+ printf("Testing with ROM %d, 0xID %04x\n",romNr,id);
+ switch(romNr) {
+ case 3: emu=new cEmuRom3; break;
+ case 7: emu=new cEmuRom7; break;
+ case 10: emu=new cEmuRom10; break;
+ case 11: emu=new cEmuRom11; break;
+ default: printf("unsupported ROM"); return;
+ }
+ if(!emu || !emu->Init(romNr,id)) {
+ delete emu; emu=0;
+ printf("initialization failed for ROM %d\n",romNr);
+ return;
+ }
+ }
+ unsigned char ki[2];
+ if((gotKeys=emu->GetOpKeys(emmdata,ki,key0,key1))) {
+ printf("keyid: "); SDump(ki,2);
+ printf("key0: "); SDump(key0,8);
+ printf("key1: "); SDump(key1,8);
+ keyId=(ki[0]<<8)+ki[1];
+ printf("got keys for %04X (ROM %d)\n",keyId,romNr);
+ }
+ unsigned char select[3], pkset[3][15];
+ select[0]=(keyId>>8)|0x01; // always high id for ECM RSA keys
+ select[1]=keyId&0xFF;
+ select[2]=0; // type 0
+HexDump(select,3);
+ if(emu->GetPkKeys(&select[0],&pkset[0][0])) {
+ int pkKeyId=((select[0]<<8)+select[1]);
+ printf("got PK keys for %04X (ROM %d)\n",pkKeyId,romNr);
+HexDump(pkset[0],sizeof(pkset[0]));
+HexDump(pkset[1],sizeof(pkset[1]));
+HexDump(pkset[2],sizeof(pkset[2]));
+ for(int i=0; i<3; i++) {
+ CreateRSAPair(pkset[i],0,e1,n1);
+ printf("keyE1 set=%d ",i); bprint(e1);
+ printf("keyN1 set=%d ",i); bprint(n1);
+ }
+ }
+ break; // don't process other nanos
+ }
+ else if(emmdata[i]==0x60) { // NULL nano
+ i+=2;
+ }
+ else if(emmdata[i]==0x00) {
+ i++;
+ }
+ else if(emmdata[i]==0x81) {
+ i++;
+ }
+ else if(emmdata[i]==0x83) {
+ keyId=(emmdata[i+1]<<8)+emmdata[i+2];
+ i+=3;
+ }
+ else if(emmdata[i]==0x42) { // plain Key
+ if(emmdata[i+1]==0x05) memcpy(key0,&emmdata[i+2],sizeof(key0));
+ else memcpy(key1,&emmdata[i+2],sizeof(key1));
+ i+=10;
+ if(++nrKeys==2) {
+ gotKeys=true;
+ printf("got keys for %04X (plain)\n",keyId);
+ break;
+ }
+ }
+ else {
+ printf("ignored nano %02x\n",emmdata[i]);
+ break;
+ }
+ }
+
+ delete emu;
+}
+
+
+int main(int argc, char *argv[])
+{
+ if(argc<3) {
+ printf("usage: %s <plugin-dir> <id> <rawemm-file>\n",argv[0]);
+ return 1;
+ }
+
+ InitAll(argv[1]);
+ unsigned char emmdata[64];
+ int emmlen=ReadRaw(argv[3],emmdata,sizeof(emmdata));
+ if(emmlen!=64) {
+ printf("bad rawemm file format\n");
+ return 1;
+ }
+ const int id=strtol(argv[2],0,0);
+ cTestNagra test;
+ test.Process(id,emmdata);
+ return 0;
+}
--- /dev/null
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#define TESTER
+#include "crypto.h"
+#include "data.h"
+#include "system-common.h"
+#include "systems/nagra/cpu.c"
+#include "systems/nagra/nagra2.c"
+
+#include "compat.h"
+
+static const unsigned char key3des[16] = { };
+
+void Emm(unsigned char *emmdata, int cmdLen, int id)
+{
+ cN2Prov *emmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+ if(emmP) printf("provider %04x capabilities%s%s%s%s\n",id,
+ emmP->HasFlags(N2FLAG_MECM) ?" MECM":"",
+ emmP->HasFlags(N2FLAG_Bx) ?" Bx":"",
+ emmP->HasFlags(N2FLAG_POSTAU) ?" POSTPROCAU":"",
+ emmP->HasFlags(N2FLAG_INV) ?" INVCW":"");
+
+ for(int i=8+2+4+4; i<cmdLen-22; ) {
+printf("%02x: nano %02x\n",i,emmdata[i]);
+ switch(emmdata[i]) {
+ case 0x42: // plain Key update
+ if(emmdata[i+2]==0x10 && (emmdata[i+3]&0xBF)==0x06 &&
+ (emmdata[i+4]&0xF8)==0x08 && emmdata[i+5]==0x00 && emmdata[i+6]==0x10) {
+ if(!emmP || emmP->PostProcAU(id,&emmdata[i])) {
+ printf("key%02x: ",(emmdata[i+3]&0x40)>>6);
+ SDump(&emmdata[i+7],16);
+ }
+ }
+ i+=23;
+ break;
+ case 0xE0: // DN key update
+ if(emmdata[i+1]==0x25) {
+ printf("key%02x: ",(emmdata[i+16]&0x40)>>6);
+ SDump(&emmdata[i+23],16);
+ }
+ i+=39;
+ break;
+ case 0x83: // change data prov. id
+ id=(emmdata[i+1]<<8)|emmdata[i+2];
+ printf("keyid: %04x\n",id);
+ i+=3;
+ break;
+ case 0xA4: // conditional (always no match assumed for now)
+ i+=emmdata[i+1]+2+4;
+ break;
+ case 0xA6:
+ i+=emmdata[i+1]+1;
+ break;
+ case 0x13:
+ case 0x14:
+ case 0x15:
+ i+=4;
+ break;
+ case 0xB0 ... 0xBF: // Update with ROM CPU code
+ {
+ int bx=emmdata[i]&15;
+ if(!emmP || !emmP->HasFlags(N2FLAG_Bx)) {
+ printf("B%X for provider %04x not supported\n",bx,id);
+ i=cmdLen;
+ break;
+ }
+ int r;
+ if((r=emmP->ProcessBx(emmdata,cmdLen,i+1))>0)
+ i+=r;
+ else {
+ printf("B%X executing failed for %04x\n",bx,id);
+ i=cmdLen;
+ }
+ break;
+ }
+ case 0xE3: // Eeprom update
+ i+=emmdata[i+4]+4;
+ break;
+ case 0xE1:
+ case 0xE2:
+ case 0x00: // end of processing
+ i=cmdLen;
+ break;
+ default:
+ i++;
+ continue;
+ }
+ }
+}
+
+bool Ecm(unsigned char *buff, int cmdLen, int id)
+{
+ unsigned char cw[16];
+
+ cN2Prov *ecmP=cN2Providers::GetProv(id,N2FLAG_NONE);
+ if(ecmP) printf("provider %04x capabilities%s%s%s%s\n",id,
+ ecmP->HasFlags(N2FLAG_MECM) ?" MECM":"",
+ ecmP->HasFlags(N2FLAG_Bx) ?" Bx":"",
+ ecmP->HasFlags(N2FLAG_POSTAU) ?" POSTPROCAU":"",
+ ecmP->HasFlags(N2FLAG_INV) ?" INVCW":"");
+
+ int l=0, mecmAlgo=0;
+ for(int i=16; i<cmdLen-10 && l!=3; ) {
+printf("%02x: nano %02x\n",i,buff[i]);
+ switch(buff[i]) {
+ case 0x10:
+ case 0x11:
+ if(buff[i+1]==0x09) {
+ int s=(~buff[i])&1;
+ mecmAlgo=buff[i+2]&0x60;
+ memcpy(cw+(s<<3),&buff[i+3],8);
+ i+=11; l|=(s+1);
+ }
+ else {
+ printf("bad length %d in CW nano %02x\n",buff[i+1],buff[i]);
+ i++;
+ }
+ break;
+ case 0x00:
+ i+=2; break;
+ case 0x13 ... 0x15:
+ i+=4; break;
+ case 0x30 ... 0x36:
+ case 0xB0:
+ i+=buff[i+1]+2;
+ break;
+ default:
+ i++;
+ continue;
+ }
+ }
+ if(l!=3) return false;
+ if(mecmAlgo>0) {
+ if(ecmP && ecmP->HasFlags(N2FLAG_MECM)) {
+ if(!ecmP->MECM(buff[15],mecmAlgo,cw)) return false;
+ }
+ else { printf("MECM for provider %04x not supported\n",id); return false; }
+ }
+ if(ecmP) ecmP->SwapCW(cw);
+ printf("resulting CW: "); SDump(cw,16);
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ if(argc<4) {
+ printf("usage: %s <plugin-dir> <id> <ECM/EMM> <raw-file>\n",argv[0]);
+ return 1;
+ }
+
+ int mode;
+ if(!strcasecmp(argv[3],"ECM")) mode=1;
+ else if(!strcasecmp(argv[3],"EMM")) mode=2;
+ else {
+ printf("bad mode '%s'\n",argv[3]);
+ return 1;
+ }
+
+ InitAll(argv[1]);
+ unsigned char data[256];
+ int len=ReadRaw(argv[4],data,sizeof(data));
+ if((mode==1 && len!=64) || (mode==2 && len!=96)) {
+ printf("bad raw file format\n");
+ return 1;
+ }
+
+ int id=strtol(argv[2],0,0);
+ if(mode==1) Ecm(data,len,id);
+ else if(mode==2) Emm(data,len,id);
+}
--- /dev/null
+/*
+ * Softcam plugin to VDR (C++)
+ *
+ * This code 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 code 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ * Or, point your browser to http://www.gnu.org/copyleft/gpl.html
+ */
+
+#ifndef ___VERSION_H
+#define ___VERSION_H
+
+// all release versions must end with 0xFF !!
+#define SCVERSNUM 0x000800FF
+#define SCVERSION "0.8.0"
+
+extern const char *ScVersion;
+
+#endif