mirror of
https://github.com/arendst/Tasmota.git
synced 2025-07-24 03:06:33 +00:00
Added Wireguard VPN (#23347)
This commit is contained in:
parent
723684bb06
commit
94652ad6ed
@ -25,9 +25,6 @@
|
||||
#include <stdlib.h>
|
||||
#include <Arduino.h>
|
||||
|
||||
// #define strcmp_P(x, y) strcmp(x,y)
|
||||
// #define strcasecmp_P(x,y) strcasecmp(x,y)
|
||||
// #define pgm_read_byte(x) (*(uint8_t*)(x))
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
|
||||
#endif
|
||||
|
504
lib/lib_ssl/IniFile-Tasmota/LICENSE
Normal file
504
lib/lib_ssl/IniFile-Tasmota/LICENSE
Normal file
@ -0,0 +1,504 @@
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
Version 2.1, February 1999
|
||||
|
||||
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
(This is the first released version of the Lesser GPL. It also counts
|
||||
as the successor of the GNU Library Public License, version 2, hence
|
||||
the version number 2.1.)
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
Licenses are intended to guarantee your freedom to share and change
|
||||
free software--to make sure the software is free for all its users.
|
||||
|
||||
This license, the Lesser General Public License, applies to some
|
||||
specially designated software packages--typically libraries--of the
|
||||
Free Software Foundation and other authors who decide to use it. You
|
||||
can use it too, but we suggest you first think carefully about whether
|
||||
this license or the ordinary General Public License is the better
|
||||
strategy to use in any particular case, based on the explanations below.
|
||||
|
||||
When we speak of free software, we are referring to freedom of use,
|
||||
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 and use pieces of
|
||||
it in new free programs; and that you are informed that you can do
|
||||
these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
distributors to deny you these rights or to ask you to surrender these
|
||||
rights. These restrictions translate to certain responsibilities for
|
||||
you if you distribute copies of the library or if you modify it.
|
||||
|
||||
For example, if you distribute copies of the library, whether gratis
|
||||
or for a fee, you must give the recipients all the rights that we gave
|
||||
you. You must make sure that they, too, receive or can get the source
|
||||
code. If you link other code with the library, you must provide
|
||||
complete object files to the recipients, so that they can relink them
|
||||
with the library after making changes to the library and recompiling
|
||||
it. And you must show them these terms so they know their rights.
|
||||
|
||||
We protect your rights with a two-step method: (1) we copyright the
|
||||
library, and (2) we offer you this license, which gives you legal
|
||||
permission to copy, distribute and/or modify the library.
|
||||
|
||||
To protect each distributor, we want to make it very clear that
|
||||
there is no warranty for the free library. Also, if the library is
|
||||
modified by someone else and passed on, the recipients should know
|
||||
that what they have is not the original version, so that the original
|
||||
author's reputation will not be affected by problems that might be
|
||||
introduced by others.
|
||||
|
||||
Finally, software patents pose a constant threat to the existence of
|
||||
any free program. We wish to make sure that a company cannot
|
||||
effectively restrict the users of a free program by obtaining a
|
||||
restrictive license from a patent holder. Therefore, we insist that
|
||||
any patent license obtained for a version of the library must be
|
||||
consistent with the full freedom of use specified in this license.
|
||||
|
||||
Most GNU software, including some libraries, is covered by the
|
||||
ordinary GNU General Public License. This license, the GNU Lesser
|
||||
General Public License, applies to certain designated libraries, and
|
||||
is quite different from the ordinary General Public License. We use
|
||||
this license for certain libraries in order to permit linking those
|
||||
libraries into non-free programs.
|
||||
|
||||
When a program is linked with a library, whether statically or using
|
||||
a shared library, the combination of the two is legally speaking a
|
||||
combined work, a derivative of the original library. The ordinary
|
||||
General Public License therefore permits such linking only if the
|
||||
entire combination fits its criteria of freedom. The Lesser General
|
||||
Public License permits more lax criteria for linking other code with
|
||||
the library.
|
||||
|
||||
We call this license the "Lesser" General Public License because it
|
||||
does Less to protect the user's freedom than the ordinary General
|
||||
Public License. It also provides other free software developers Less
|
||||
of an advantage over competing non-free programs. These disadvantages
|
||||
are the reason we use the ordinary General Public License for many
|
||||
libraries. However, the Lesser license provides advantages in certain
|
||||
special circumstances.
|
||||
|
||||
For example, on rare occasions, there may be a special need to
|
||||
encourage the widest possible use of a certain library, so that it becomes
|
||||
a de-facto standard. To achieve this, non-free programs must be
|
||||
allowed to use the library. A more frequent case is that a free
|
||||
library does the same job as widely used non-free libraries. In this
|
||||
case, there is little to gain by limiting the free library to free
|
||||
software only, so we use the Lesser General Public License.
|
||||
|
||||
In other cases, permission to use a particular library in non-free
|
||||
programs enables a greater number of people to use a large body of
|
||||
free software. For example, permission to use the GNU C Library in
|
||||
non-free programs enables many more people to use the whole GNU
|
||||
operating system, as well as its variant, the GNU/Linux operating
|
||||
system.
|
||||
|
||||
Although the Lesser General Public License is Less protective of the
|
||||
users' freedom, it does ensure that the user of a program that is
|
||||
linked with the Library has the freedom and the wherewithal to run
|
||||
that program using a modified version of the Library.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow. Pay close attention to the difference between a
|
||||
"work based on the library" and a "work that uses the library". The
|
||||
former contains code derived from the library, whereas the latter must
|
||||
be combined with the library in order to run.
|
||||
|
||||
GNU LESSER GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License Agreement applies to any software library or other
|
||||
program which contains a notice placed by the copyright holder or
|
||||
other authorized party saying it may be distributed under the terms of
|
||||
this Lesser General Public License (also called "this License").
|
||||
Each licensee is addressed as "you".
|
||||
|
||||
A "library" means a collection of software functions and/or data
|
||||
prepared so as to be conveniently linked with application programs
|
||||
(which use some of those functions and data) to form executables.
|
||||
|
||||
The "Library", below, refers to any such software library or work
|
||||
which has been distributed under these terms. A "work based on the
|
||||
Library" means either the Library or any derivative work under
|
||||
copyright law: that is to say, a work containing the Library or a
|
||||
portion of it, either verbatim or with modifications and/or translated
|
||||
straightforwardly into another language. (Hereinafter, translation is
|
||||
included without limitation in the term "modification".)
|
||||
|
||||
"Source code" for a work means the preferred form of the work for
|
||||
making modifications to it. For a library, 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 library.
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running a program using the Library is not restricted, and output from
|
||||
such a program is covered only if its contents constitute a work based
|
||||
on the Library (independent of the use of the Library in a tool for
|
||||
writing it). Whether that is true depends on what the Library does
|
||||
and what the program that uses the Library does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Library's
|
||||
complete 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 distribute a copy of this License along with the
|
||||
Library.
|
||||
|
||||
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 Library or any portion
|
||||
of it, thus forming a work based on the Library, 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) The modified work must itself be a software library.
|
||||
|
||||
b) You must cause the files modified to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
c) You must cause the whole of the work to be licensed at no
|
||||
charge to all third parties under the terms of this License.
|
||||
|
||||
d) If a facility in the modified Library refers to a function or a
|
||||
table of data to be supplied by an application program that uses
|
||||
the facility, other than as an argument passed when the facility
|
||||
is invoked, then you must make a good faith effort to ensure that,
|
||||
in the event an application does not supply such function or
|
||||
table, the facility still operates, and performs whatever part of
|
||||
its purpose remains meaningful.
|
||||
|
||||
(For example, a function in a library to compute square roots has
|
||||
a purpose that is entirely well-defined independent of the
|
||||
application. Therefore, Subsection 2d requires that any
|
||||
application-supplied function or table used by this function must
|
||||
be optional: if the application does not supply it, the square
|
||||
root function must still compute square roots.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Library,
|
||||
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 Library, 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 Library.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Library
|
||||
with the Library (or with a work based on the Library) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may opt to apply the terms of the ordinary GNU General Public
|
||||
License instead of this License to a given copy of the Library. To do
|
||||
this, you must alter all the notices that refer to this License, so
|
||||
that they refer to the ordinary GNU General Public License, version 2,
|
||||
instead of to this License. (If a newer version than version 2 of the
|
||||
ordinary GNU General Public License has appeared, then you can specify
|
||||
that version instead if you wish.) Do not make any other change in
|
||||
these notices.
|
||||
|
||||
Once this change is made in a given copy, it is irreversible for
|
||||
that copy, so the ordinary GNU General Public License applies to all
|
||||
subsequent copies and derivative works made from that copy.
|
||||
|
||||
This option is useful when you wish to copy part of the code of
|
||||
the Library into a program that is not a library.
|
||||
|
||||
4. You may copy and distribute the Library (or a portion or
|
||||
derivative of it, under Section 2) in object code or executable form
|
||||
under the terms of Sections 1 and 2 above provided that you 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.
|
||||
|
||||
If distribution of 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 satisfies the requirement to
|
||||
distribute the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
5. A program that contains no derivative of any portion of the
|
||||
Library, but is designed to work with the Library by being compiled or
|
||||
linked with it, is called a "work that uses the Library". Such a
|
||||
work, in isolation, is not a derivative work of the Library, and
|
||||
therefore falls outside the scope of this License.
|
||||
|
||||
However, linking a "work that uses the Library" with the Library
|
||||
creates an executable that is a derivative of the Library (because it
|
||||
contains portions of the Library), rather than a "work that uses the
|
||||
library". The executable is therefore covered by this License.
|
||||
Section 6 states terms for distribution of such executables.
|
||||
|
||||
When a "work that uses the Library" uses material from a header file
|
||||
that is part of the Library, the object code for the work may be a
|
||||
derivative work of the Library even though the source code is not.
|
||||
Whether this is true is especially significant if the work can be
|
||||
linked without the Library, or if the work is itself a library. The
|
||||
threshold for this to be true is not precisely defined by law.
|
||||
|
||||
If such an object file uses only numerical parameters, data
|
||||
structure layouts and accessors, and small macros and small inline
|
||||
functions (ten lines or less in length), then the use of the object
|
||||
file is unrestricted, regardless of whether it is legally a derivative
|
||||
work. (Executables containing this object code plus portions of the
|
||||
Library will still fall under Section 6.)
|
||||
|
||||
Otherwise, if the work is a derivative of the Library, you may
|
||||
distribute the object code for the work under the terms of Section 6.
|
||||
Any executables containing that work also fall under Section 6,
|
||||
whether or not they are linked directly with the Library itself.
|
||||
|
||||
6. As an exception to the Sections above, you may also combine or
|
||||
link a "work that uses the Library" with the Library to produce a
|
||||
work containing portions of the Library, and distribute that work
|
||||
under terms of your choice, provided that the terms permit
|
||||
modification of the work for the customer's own use and reverse
|
||||
engineering for debugging such modifications.
|
||||
|
||||
You must give prominent notice with each copy of the work that the
|
||||
Library is used in it and that the Library and its use are covered by
|
||||
this License. You must supply a copy of this License. If the work
|
||||
during execution displays copyright notices, you must include the
|
||||
copyright notice for the Library among them, as well as a reference
|
||||
directing the user to the copy of this License. Also, you must do one
|
||||
of these things:
|
||||
|
||||
a) Accompany the work with the complete corresponding
|
||||
machine-readable source code for the Library including whatever
|
||||
changes were used in the work (which must be distributed under
|
||||
Sections 1 and 2 above); and, if the work is an executable linked
|
||||
with the Library, with the complete machine-readable "work that
|
||||
uses the Library", as object code and/or source code, so that the
|
||||
user can modify the Library and then relink to produce a modified
|
||||
executable containing the modified Library. (It is understood
|
||||
that the user who changes the contents of definitions files in the
|
||||
Library will not necessarily be able to recompile the application
|
||||
to use the modified definitions.)
|
||||
|
||||
b) Use a suitable shared library mechanism for linking with the
|
||||
Library. A suitable mechanism is one that (1) uses at run time a
|
||||
copy of the library already present on the user's computer system,
|
||||
rather than copying library functions into the executable, and (2)
|
||||
will operate properly with a modified version of the library, if
|
||||
the user installs one, as long as the modified version is
|
||||
interface-compatible with the version that the work was made with.
|
||||
|
||||
c) Accompany the work with a written offer, valid for at
|
||||
least three years, to give the same user the materials
|
||||
specified in Subsection 6a, above, for a charge no more
|
||||
than the cost of performing this distribution.
|
||||
|
||||
d) If distribution of the work is made by offering access to copy
|
||||
from a designated place, offer equivalent access to copy the above
|
||||
specified materials from the same place.
|
||||
|
||||
e) Verify that the user has already received a copy of these
|
||||
materials or that you have already sent this user a copy.
|
||||
|
||||
For an executable, the required form of the "work that uses the
|
||||
Library" must include any data and utility programs needed for
|
||||
reproducing the executable from it. However, as a special exception,
|
||||
the materials to be 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.
|
||||
|
||||
It may happen that this requirement contradicts the license
|
||||
restrictions of other proprietary libraries that do not normally
|
||||
accompany the operating system. Such a contradiction means you cannot
|
||||
use both them and the Library together in an executable that you
|
||||
distribute.
|
||||
|
||||
7. You may place library facilities that are a work based on the
|
||||
Library side-by-side in a single library together with other library
|
||||
facilities not covered by this License, and distribute such a combined
|
||||
library, provided that the separate distribution of the work based on
|
||||
the Library and of the other library facilities is otherwise
|
||||
permitted, and provided that you do these two things:
|
||||
|
||||
a) Accompany the combined library with a copy of the same work
|
||||
based on the Library, uncombined with any other library
|
||||
facilities. This must be distributed under the terms of the
|
||||
Sections above.
|
||||
|
||||
b) Give prominent notice with the combined library of the fact
|
||||
that part of it is a work based on the Library, and explaining
|
||||
where to find the accompanying uncombined form of the same work.
|
||||
|
||||
8. You may not copy, modify, sublicense, link with, or distribute
|
||||
the Library except as expressly provided under this License. Any
|
||||
attempt otherwise to copy, modify, sublicense, link with, or
|
||||
distribute the Library 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.
|
||||
|
||||
9. 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 Library or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Library (or any work based on the
|
||||
Library), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Library or works based on it.
|
||||
|
||||
10. Each time you redistribute the Library (or any work based on the
|
||||
Library), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute, link with or modify the Library
|
||||
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 with
|
||||
this License.
|
||||
|
||||
11. 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 Library at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Library 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 Library.
|
||||
|
||||
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.
|
||||
|
||||
12. If the distribution and/or use of the Library is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Library 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.
|
||||
|
||||
13. The Free Software Foundation may publish revised and/or new
|
||||
versions of the Lesser 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 Library
|
||||
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 Library does not specify a
|
||||
license version number, you may choose any version ever published by
|
||||
the Free Software Foundation.
|
||||
|
||||
14. If you wish to incorporate parts of the Library into other free
|
||||
programs whose distribution conditions are incompatible with these,
|
||||
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
|
||||
|
||||
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
|
||||
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
|
||||
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
|
||||
OTHER PARTIES PROVIDE THE LIBRARY "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
|
||||
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
|
||||
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
|
||||
|
||||
16. 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 LIBRARY 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
|
||||
LIBRARY (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 LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
|
||||
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
|
||||
DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Libraries
|
||||
|
||||
If you develop a new library, and you want it to be of the greatest
|
||||
possible use to the public, we recommend making it free software that
|
||||
everyone can redistribute and change. You can do so by permitting
|
||||
redistribution under these terms (or, alternatively, under the terms of the
|
||||
ordinary General Public License).
|
||||
|
||||
To apply these terms, attach the following notices to the library. 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.
|
||||
|
||||
{description}
|
||||
Copyright (C) {year} {fullname}
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library 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
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
|
||||
USA
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the library, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the
|
||||
library `Frob' (a library for tweaking knobs) written by James Random
|
||||
Hacker.
|
||||
|
||||
{signature of Ty Coon}, 1 April 1990
|
||||
Ty Coon, President of Vice
|
||||
|
||||
That's all there is to it!
|
101
lib/lib_ssl/IniFile-Tasmota/README.md
Normal file
101
lib/lib_ssl/IniFile-Tasmota/README.md
Normal file
@ -0,0 +1,101 @@
|
||||
# IniFile
|
||||
|
||||
IniFile is an Arduino library for reading ini files. The format is
|
||||
similar to that seen in Microsoft `.ini` files but the implementation
|
||||
is completely independent. IniFile is designed to use minimal memory
|
||||
requirements, and the only buffer used is one supplied by the user,
|
||||
thus the user remains in charge of memory usage.
|
||||
|
||||
The ini file is separated into sections, where the section names are
|
||||
written inside square brackets. If you don't wish to use sections then
|
||||
pass a `NULL` pointer for the section name. Under each section are a
|
||||
set of key-value pairs, separated by an equals sign (`=`). Spaces
|
||||
around keys and values are ignored, but extra spaces inside the key
|
||||
are significant. Whitespace inside the value string is preserved; if
|
||||
leading or trailing whitespace is important you must quote the value
|
||||
string inside the ini file, and you must strip out the quotes
|
||||
yourself. If multiple entries for the same key exist inside the
|
||||
selected section (if using) then only the first value is returned. If
|
||||
a section is defined more than once only the first is used. The .ini
|
||||
file can contain comments, which begin with a semicolon (`;`) or hash
|
||||
(`#`). The user-supplied buffer must be large enough to accomodate the
|
||||
longest line in the file.
|
||||
|
||||
## Example file format
|
||||
|
||||
; Semi-colon comment
|
||||
[network]
|
||||
mac = 01:23:45:67:89:AB
|
||||
|
||||
# hash comment, leading spaces below
|
||||
gateway = 192.168.1.1
|
||||
|
||||
# extraneous spaces before and after key and value
|
||||
ip = 192.168.1.2
|
||||
|
||||
hosts allow = example.com
|
||||
|
||||
# A similarly-named section
|
||||
[network2]
|
||||
mac = ee:ee:ee:ee:ee:ee
|
||||
subnet mask=255.255.255.0
|
||||
|
||||
; Extra whitespace around the key and value is permitted
|
||||
; (and ignored)
|
||||
hosts allow = sloppy.example.com
|
||||
|
||||
[misc]
|
||||
|
||||
string = 123456789012345678901234567890123456789001234567890
|
||||
string2 = a string with spaces in it
|
||||
|
||||
; This section is a repeat of en existing section and will be ignored.
|
||||
[network]
|
||||
mac = 01:23:45:67:89:ab
|
||||
ip = 192.168.1.2
|
||||
gateway = 192.168.1.1
|
||||
|
||||
|
||||
## Write support
|
||||
|
||||
Write support is a feature that has been requested on several
|
||||
occasions but as I am no longer using the IniFile library I will not
|
||||
add this feature. For anyone planning to add such support the
|
||||
information below may be useful.
|
||||
|
||||
One goal of the `IniFile` implementation was to limit the amount of
|
||||
memory required. For use in embedded systems `malloc` and `new` are
|
||||
deliberately not used. Another goal was that tasks which take a longer
|
||||
duration were broken down into smaller chunks of work, eg
|
||||
`IniFile::getValue(const char* section, const char* key, char* buffer,
|
||||
int len, IniFileState &state)`. This was because I wanted my `WwwServer`
|
||||
library, which uses `IniFile`, to avoid interfering with time-critical
|
||||
code.
|
||||
|
||||
I don't think that write support can meet the time-critical goal but
|
||||
that doesn't prevent its inclusion. I think the way I would choose to
|
||||
implement write support is to use `IniFile::findKey()` to find where the
|
||||
desired key is located in the file. I'd then copy everything up to
|
||||
that point to a temporary file, insert a line for the value and new
|
||||
key, skip the current line in the existing file (using
|
||||
`IniFile::readline()`) and then write out the reminder of the existing
|
||||
file into the temporary file. I'd like to move or rename the temporary
|
||||
file over the existing file but the Arduino SD library doesn't provide
|
||||
this functionality; I'd probably just copy the temporary file over the
|
||||
old one and then delete the temporary one.
|
||||
|
||||
The code has been written under a standard Linux environment, using a
|
||||
compatibility header file to mimic the SD library. This was far more
|
||||
convenient than doing everything on the Arduino and enabled me to use
|
||||
`gdb` to debug the code and core dumps. You'll find `arduino_compat.h`
|
||||
inside the test directory, along with a `Makefile` which can be used for
|
||||
regression testing. Any proposed changes must pass the regression
|
||||
tests.
|
||||
|
||||
## Contributors
|
||||
|
||||
* [Steve Marple](https://github.com/stevemarple)
|
||||
* [per1234](https://github.com/per1234)
|
||||
* [OscarVanL](https://github.com/OscarVanL)
|
||||
* [MikuX2](https://github.com/toybox01)
|
||||
* [kaixxx](https://github.com/kaixxx)
|
@ -0,0 +1,135 @@
|
||||
#include <SD.h>
|
||||
|
||||
#include <SPI.h>
|
||||
#include <IPAddress.h>
|
||||
#include <IniFile.h>
|
||||
|
||||
// The select pin used for the SD card
|
||||
#define SD_SELECT 10
|
||||
//#define ETHERNET_SELECT 10
|
||||
|
||||
void printErrorMessage(uint8_t e, bool eol = true)
|
||||
{
|
||||
switch (e) {
|
||||
case IniFile::errorNoError:
|
||||
Serial.print("no error");
|
||||
break;
|
||||
case IniFile::errorFileNotFound:
|
||||
Serial.print("file not found");
|
||||
break;
|
||||
case IniFile::errorFileNotOpen:
|
||||
Serial.print("file not open");
|
||||
break;
|
||||
case IniFile::errorBufferTooSmall:
|
||||
Serial.print("buffer too small");
|
||||
break;
|
||||
case IniFile::errorSeekError:
|
||||
Serial.print("seek error");
|
||||
break;
|
||||
case IniFile::errorSectionNotFound:
|
||||
Serial.print("section not found");
|
||||
break;
|
||||
case IniFile::errorKeyNotFound:
|
||||
Serial.print("key not found");
|
||||
break;
|
||||
case IniFile::errorEndOfFile:
|
||||
Serial.print("end of file");
|
||||
break;
|
||||
case IniFile::errorUnknownError:
|
||||
Serial.print("unknown error");
|
||||
break;
|
||||
default:
|
||||
Serial.print("unknown error value");
|
||||
break;
|
||||
}
|
||||
if (eol)
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Configure all of the SPI select pins as outputs and make SPI
|
||||
// devices inactive, otherwise the earlier init routines may fail
|
||||
// for devices which have not yet been configured.
|
||||
pinMode(SD_SELECT, OUTPUT);
|
||||
digitalWrite(SD_SELECT, HIGH); // disable SD card
|
||||
|
||||
// pinMode(ETHERNET_SELECT, OUTPUT);
|
||||
// digitalWrite(ETHERNET_SELECT, HIGH); // disable Ethernet
|
||||
|
||||
const size_t bufferLen = 80;
|
||||
char buffer[bufferLen];
|
||||
|
||||
const char *filename = "/lunch.ini";
|
||||
Serial.begin(9600);
|
||||
SPI.begin();
|
||||
if (!SD.begin(SD_SELECT))
|
||||
while (1)
|
||||
Serial.println("SD.begin() failed");
|
||||
|
||||
IniFile ini(filename);
|
||||
if (!ini.open()) {
|
||||
Serial.print("Ini file ");
|
||||
Serial.print(filename);
|
||||
Serial.println(" does not exist");
|
||||
// Cannot do anything else
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
Serial.println("Ini file exists");
|
||||
|
||||
// Check the file is valid. This can be used to warn if any lines
|
||||
// are longer than the buffer.
|
||||
if (!ini.validate(buffer, bufferLen)) {
|
||||
Serial.print("ini file ");
|
||||
Serial.print(ini.getFilename());
|
||||
Serial.print(" not valid: ");
|
||||
printErrorMessage(ini.getError());
|
||||
// Cannot do anything else
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
// Browse through all sections and print contents:
|
||||
IniFileState state;
|
||||
char sectName[bufferLen];
|
||||
Serial.println();
|
||||
|
||||
while (ini.browseSections(sectName, bufferLen, state)) {
|
||||
Serial.print("> ");
|
||||
Serial.print(sectName);
|
||||
|
||||
if (ini.getValue(sectName, "meal", buffer, bufferLen)) {
|
||||
Serial.print(" eats ");
|
||||
Serial.print(buffer);
|
||||
} else
|
||||
Serial.print(" eats nothing");
|
||||
|
||||
if (ini.getValue(sectName, "drinks", buffer, bufferLen)) {
|
||||
Serial.print(", drinks ");
|
||||
Serial.print(buffer);
|
||||
} else
|
||||
Serial.print(", drinks nothing");
|
||||
|
||||
if (ini.getValue(sectName, "dessert", buffer, bufferLen)) {
|
||||
Serial.print(" and has ");
|
||||
Serial.print(buffer);
|
||||
Serial.println(" for dessert.");
|
||||
} else
|
||||
Serial.println(" and has no dessert.");
|
||||
}
|
||||
|
||||
// finished!
|
||||
Serial.println();
|
||||
printErrorMessage(ini.getError());
|
||||
// Cannot do anything else
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
# IniFile browsing example
|
||||
|
||||
Shows how to browse through an ini file with an unknown number of sections
|
||||
which all contain the same elements. This can be useful to store different
|
||||
profiles which are selectable on your device.
|
||||
|
||||
|
||||
## Instructions for use
|
||||
|
||||
* Copy the `lunch.ini` file to the root directory of your (micro)SD card.
|
||||
* Modify the `IniBrowseExample.ino` file so that `SD_SELECT` defines
|
||||
the correct pin number.
|
||||
* Compile and upload the sketch.
|
||||
|
||||
|
||||
## Expected output
|
||||
|
||||
It may take a few seconds from the sketch starting before anything is
|
||||
printed to the serial port, be patient. If the sketch runs correctly
|
||||
the output should appear as below:
|
||||
|
||||
Ini file exists
|
||||
|
||||
> Karen eats burger, drinks beer and has chocolate for dessert.
|
||||
> Peter eats falafel, drinks tea without milk and has vegan icecream for dessert.
|
||||
> Noel eats sushi, drinks water and has no dessert.
|
||||
> Jessica eats sandwich, drinks nothing and has muffin for dessert.
|
||||
|
||||
end of file
|
||||
|
||||
If the SD card is missing or cannot be read the sketch will print:
|
||||
|
||||
SD.begin() failed
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
; A list of settings/profiles stored as sections.
|
||||
; Each profile has the same elements.
|
||||
|
||||
[Karen]
|
||||
meal = burger
|
||||
drinks = beer
|
||||
dessert = chocolate
|
||||
|
||||
[Peter]
|
||||
meal = falafel
|
||||
drinks = tea without milk
|
||||
dessert = vegan icecream
|
||||
|
||||
[Noel]
|
||||
meal = sushi
|
||||
drinks = water
|
||||
; note: dessert is missing here
|
||||
|
||||
[Jessica]
|
||||
meal = sandwich
|
||||
; no drinks
|
||||
dessert = muffin
|
@ -0,0 +1,143 @@
|
||||
#include <SD.h>
|
||||
|
||||
#include <SPI.h>
|
||||
#include <IPAddress.h>
|
||||
#include <IniFile.h>
|
||||
|
||||
// The select pin used for the SD card
|
||||
//#define SD_SELECT 4
|
||||
#define SD_SELECT 22
|
||||
#define ETHERNET_SELECT 10
|
||||
|
||||
void printErrorMessage(uint8_t e, bool eol = true)
|
||||
{
|
||||
switch (e) {
|
||||
case IniFile::errorNoError:
|
||||
Serial.print("no error");
|
||||
break;
|
||||
case IniFile::errorFileNotFound:
|
||||
Serial.print("file not found");
|
||||
break;
|
||||
case IniFile::errorFileNotOpen:
|
||||
Serial.print("file not open");
|
||||
break;
|
||||
case IniFile::errorBufferTooSmall:
|
||||
Serial.print("buffer too small");
|
||||
break;
|
||||
case IniFile::errorSeekError:
|
||||
Serial.print("seek error");
|
||||
break;
|
||||
case IniFile::errorSectionNotFound:
|
||||
Serial.print("section not found");
|
||||
break;
|
||||
case IniFile::errorKeyNotFound:
|
||||
Serial.print("key not found");
|
||||
break;
|
||||
case IniFile::errorEndOfFile:
|
||||
Serial.print("end of file");
|
||||
break;
|
||||
case IniFile::errorUnknownError:
|
||||
Serial.print("unknown error");
|
||||
break;
|
||||
default:
|
||||
Serial.print("unknown error value");
|
||||
break;
|
||||
}
|
||||
if (eol)
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
// Configure all of the SPI select pins as outputs and make SPI
|
||||
// devices inactive, otherwise the earlier init routines may fail
|
||||
// for devices which have not yet been configured.
|
||||
pinMode(SD_SELECT, OUTPUT);
|
||||
digitalWrite(SD_SELECT, HIGH); // disable SD card
|
||||
|
||||
pinMode(ETHERNET_SELECT, OUTPUT);
|
||||
digitalWrite(ETHERNET_SELECT, HIGH); // disable Ethernet
|
||||
|
||||
const size_t bufferLen = 80;
|
||||
char buffer[bufferLen];
|
||||
|
||||
const char *filename = "/net.ini";
|
||||
Serial.begin(9600);
|
||||
SPI.begin();
|
||||
if (!SD.begin(SD_SELECT))
|
||||
while (1)
|
||||
Serial.println("SD.begin() failed");
|
||||
|
||||
IniFile ini(filename);
|
||||
if (!ini.open()) {
|
||||
Serial.print("Ini file ");
|
||||
Serial.print(filename);
|
||||
Serial.println(" does not exist");
|
||||
// Cannot do anything else
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
Serial.println("Ini file exists");
|
||||
|
||||
// Check the file is valid. This can be used to warn if any lines
|
||||
// are longer than the buffer.
|
||||
if (!ini.validate(buffer, bufferLen)) {
|
||||
Serial.print("ini file ");
|
||||
Serial.print(ini.getFilename());
|
||||
Serial.print(" not valid: ");
|
||||
printErrorMessage(ini.getError());
|
||||
// Cannot do anything else
|
||||
while (1)
|
||||
;
|
||||
}
|
||||
|
||||
// Fetch a value from a key which is present
|
||||
if (ini.getValue("network", "mac", buffer, bufferLen)) {
|
||||
Serial.print("section 'network' has an entry 'mac' with value ");
|
||||
Serial.println(buffer);
|
||||
}
|
||||
else {
|
||||
Serial.print("Could not read 'mac' from section 'network', error was ");
|
||||
printErrorMessage(ini.getError());
|
||||
}
|
||||
|
||||
// Try fetching a value from a missing key (but section is present)
|
||||
if (ini.getValue("network", "nosuchkey", buffer, bufferLen)) {
|
||||
Serial.print("section 'network' has an entry 'nosuchkey' with value ");
|
||||
Serial.println(buffer);
|
||||
}
|
||||
else {
|
||||
Serial.print("Could not read 'nosuchkey' from section 'network', error was ");
|
||||
printErrorMessage(ini.getError());
|
||||
}
|
||||
|
||||
// Try fetching a key from a section which is not present
|
||||
if (ini.getValue("nosuchsection", "nosuchkey", buffer, bufferLen)) {
|
||||
Serial.print("section 'nosuchsection' has an entry 'nosuchkey' with value ");
|
||||
Serial.println(buffer);
|
||||
}
|
||||
else {
|
||||
Serial.print("Could not read 'nosuchkey' from section 'nosuchsection', error was ");
|
||||
printErrorMessage(ini.getError());
|
||||
}
|
||||
|
||||
// Fetch a boolean value
|
||||
bool allowPut; // variable where result will be stored
|
||||
bool found = ini.getValue("/upload", "allow put", buffer, bufferLen, allowPut);
|
||||
if (found) {
|
||||
Serial.print("The value of 'allow put' in section '/upload' is ");
|
||||
// Print value, converting boolean to a string
|
||||
Serial.println(allowPut ? "TRUE" : "FALSE");
|
||||
}
|
||||
else {
|
||||
Serial.print("Could not get the value of 'allow put' in section '/upload': ");
|
||||
printErrorMessage(ini.getError());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void loop()
|
||||
{
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
# IniFile example
|
||||
|
||||
## Instructions for use
|
||||
|
||||
* Copy the `net.ini` file to the root directory of your (micro)SD card.
|
||||
* Modify the `IniFileExample.ino` file so that `SD_SELECT` defines
|
||||
the correct pin number.
|
||||
* Compile and upload the sketch.
|
||||
|
||||
|
||||
## Expected output
|
||||
|
||||
It may take a few seconds from the sketch starting before anything is
|
||||
printed to the serial port, be patient. If the sketch runs correctly
|
||||
the output should appear as below:
|
||||
|
||||
|
||||
Ini file exists
|
||||
section 'network' has an entry 'mac' with value 01:23:45:67:89:AB
|
||||
Could not read 'nosuchkey' from section 'network', error was key not found
|
||||
Could not read 'nosuchkey' from section 'nosuchsection', error was section not found
|
||||
The value of 'allow put' in section '/upload' is TRUE
|
||||
|
||||
|
||||
If the SD card is missing or cannot be read the sketch will print:
|
||||
|
||||
SD.begin() failed
|
||||
|
||||
|
68
lib/lib_ssl/IniFile-Tasmota/examples/IniFileExample/net.ini
Normal file
68
lib/lib_ssl/IniFile-Tasmota/examples/IniFileExample/net.ini
Normal file
@ -0,0 +1,68 @@
|
||||
; Semi-colon comment
|
||||
[network]
|
||||
mac = 01:23:45:67:89:AB
|
||||
|
||||
# hash comment, leading spaces below
|
||||
gateway = 192.168.1.1
|
||||
|
||||
# extraneous spaces before and after key and value
|
||||
ip = 192.168.1.2
|
||||
|
||||
hosts allow = example.com
|
||||
|
||||
# A similarly-named section
|
||||
[network2]
|
||||
mac = ee:ee:ee:ee:ee:ee
|
||||
subnet mask=255.255.255.0
|
||||
|
||||
; Test extra whitespace in keys and value
|
||||
hosts allow = sloppy.example.com
|
||||
|
||||
[misc]
|
||||
|
||||
string = 123456789012345678901234567890123456789001234567890
|
||||
string2 = a string with spaces in it
|
||||
|
||||
; ini file for WwwServerExample
|
||||
|
||||
[mime types]
|
||||
default = text/plain
|
||||
htm = text/html
|
||||
bin = application/octet-stream
|
||||
pdf = application/pdf
|
||||
|
||||
[/]
|
||||
; no access to root of SD filesystem
|
||||
handler = default
|
||||
error document 403 = /errordoc/403.htm
|
||||
|
||||
[/www.ini]
|
||||
handler = default
|
||||
|
||||
[/data]
|
||||
handler = default
|
||||
|
||||
[/data/private]
|
||||
; Block access to this directory
|
||||
handler = prohibit
|
||||
error document 403 = /data/private/403.htm
|
||||
|
||||
[/data/noaccess.txt]
|
||||
; Block access to this file
|
||||
handler = prohibit
|
||||
|
||||
[/status]
|
||||
; built-in status handler
|
||||
handler = status
|
||||
|
||||
[/cgi]
|
||||
; User-defined handler
|
||||
handler = cgi
|
||||
|
||||
[/src]
|
||||
; A redirect
|
||||
handler = temporary redirect
|
||||
location = http://github.com/stevemarple/WwwServer
|
||||
|
||||
[/upload]
|
||||
allow put = true
|
9
lib/lib_ssl/IniFile-Tasmota/library.properties
Normal file
9
lib/lib_ssl/IniFile-Tasmota/library.properties
Normal file
@ -0,0 +1,9 @@
|
||||
name=IniFile
|
||||
version=modified by Tasmota, based on 1.3.0
|
||||
author=Steve Marple <stevemarple@googlemail.com>, Stephan Hadiger
|
||||
maintainer=Steve Marple <stevemarple@googlemail.com>, Stephan Hadiger
|
||||
sentence=Library to read and parse .ini files, adapted for Tasmota
|
||||
paragraph=IniFile is a library to read and parse .ini files as used by Microsoft Windows. IniFile is designed to use minimal memory requirements, and the only buffer used is one supplied by the user, thus the user remains in charge of memory usage. GNU LGPL v2.1.
|
||||
category=Other
|
||||
url=https://github.com/stevemarple/IniFile
|
||||
architectures=*
|
494
lib/lib_ssl/IniFile-Tasmota/src/IniFile.cpp
Normal file
494
lib/lib_ssl/IniFile-Tasmota/src/IniFile.cpp
Normal file
@ -0,0 +1,494 @@
|
||||
#include "IniFile.h"
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "base64.hpp"
|
||||
|
||||
//**************************************************************************************************************
|
||||
// enable AddLog support within a C++ library
|
||||
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
|
||||
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
|
||||
//**************************************************************************************************************
|
||||
|
||||
extern FS *ffsp;
|
||||
|
||||
IniFile::IniFile(File &file)
|
||||
{
|
||||
_file = file;
|
||||
}
|
||||
|
||||
IniFile::~IniFile()
|
||||
{
|
||||
//if (_file)
|
||||
// _file.close();
|
||||
}
|
||||
|
||||
IniFile::error_t IniFile::getError(void)
|
||||
{
|
||||
return _error;
|
||||
}
|
||||
|
||||
void IniFile::clearError(void)
|
||||
{
|
||||
_error = errorNoError;
|
||||
}
|
||||
|
||||
bool IniFile::getValue(const char* section, const char* key, IniFileState &state)
|
||||
{
|
||||
char *cp = nullptr;
|
||||
char *bufptr = buffer;
|
||||
bool done = false;
|
||||
if (!_file) {
|
||||
_error = errorFileNotOpen;
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (state.getValueState) {
|
||||
case IniFileState::funcUnset:
|
||||
state.getValueState = (section == NULL ? IniFileState::funcFindKey
|
||||
: IniFileState::funcFindSection);
|
||||
state.readLinePosition = 0;
|
||||
break;
|
||||
|
||||
case IniFileState::funcFindSection:
|
||||
if (findSection(section, state)) {
|
||||
if (_error != errorNoError)
|
||||
return true;
|
||||
state.getValueState = IniFileState::funcFindKey;
|
||||
}
|
||||
break;
|
||||
|
||||
case IniFileState::funcFindKey:
|
||||
if (findKey(section, key, &cp, state)) {
|
||||
if (_error != errorNoError)
|
||||
return true;
|
||||
// Found key line in correct section
|
||||
cp = skipWhiteSpace(cp);
|
||||
removeTrailingWhiteSpace(cp);
|
||||
|
||||
// Copy from cp to buffer, but the strings overlap so strcpy is out
|
||||
while (*cp != '\0')
|
||||
*bufptr++ = *cp++;
|
||||
*bufptr = '\0';
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// How did this happen?
|
||||
_error = errorUnknownError;
|
||||
done = true;
|
||||
break;
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
|
||||
bool IniFile::getValue(const char* section, const char* key)
|
||||
{
|
||||
IniFileState state;
|
||||
while (!getValue(section, key, state))
|
||||
;
|
||||
return _error == errorNoError;
|
||||
}
|
||||
|
||||
|
||||
bool IniFile::getValueStr(const char* section, const char* key, char *value, size_t vlen)
|
||||
{
|
||||
if (!getValue(section, key)) return false; // error
|
||||
if (strlen(buffer) >= vlen) return false;
|
||||
strcpy(value, buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniFile::getValueString(const char* section, const char* key, String &value)
|
||||
{
|
||||
if (!getValue(section, key)) return false; // error
|
||||
value = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// For true accept: true, yes, 1
|
||||
// For false accept: false, no, 0
|
||||
bool IniFile::getValueBool(const char* section, const char* key, bool& val)
|
||||
{
|
||||
if (!getValue(section, key))
|
||||
return false; // error
|
||||
|
||||
if (strcasecmp_P(buffer, PSTR("true")) == 0 ||
|
||||
strcasecmp_P(buffer, PSTR("yes")) == 0 ||
|
||||
strcasecmp_P(buffer, PSTR("1")) == 0) {
|
||||
val = true;
|
||||
return true;
|
||||
}
|
||||
if (strcasecmp_P(buffer, PSTR("false")) == 0 ||
|
||||
strcasecmp_P(buffer, PSTR("no")) == 0 ||
|
||||
strcasecmp_P(buffer, PSTR("0")) == 0) {
|
||||
val = false;
|
||||
return true;
|
||||
}
|
||||
return false; // does not match any known strings
|
||||
}
|
||||
|
||||
bool IniFile::getValueInt(const char* section, const char* key, int32_t& val)
|
||||
{
|
||||
if (!getValue(section, key)) return false; // error
|
||||
val = atoi(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniFile::getValueUInt16(const char* section, const char* key, uint16_t& val)
|
||||
{
|
||||
int32_t val32;
|
||||
if (!getValue(section, key)) return false; // error
|
||||
val32 = atoi(buffer);
|
||||
if (val32 < 0 || val32 > 65535) return false;
|
||||
val = (uint16_t) val32;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
bool IniFile::getValueFloat(const char* section, const char* key, float & val)
|
||||
{
|
||||
if (!getValue(section, key))
|
||||
return false; // error
|
||||
|
||||
char *endptr;
|
||||
float tmp = strtod(buffer, &endptr);
|
||||
if (endptr == buffer)
|
||||
return false; // no conversion
|
||||
if (*endptr == '\0') {
|
||||
val = tmp;
|
||||
return true; // valid conversion
|
||||
}
|
||||
// buffer has trailing non-numeric characters, and since the buffer
|
||||
// already had whitespace removed discard the entire results
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IniFile::getIPAddress(const char* section, const char* key, ip_addr_t *ip)
|
||||
{
|
||||
if (!getValue(section, key)) return false; // error
|
||||
IPAddress ipaddr;
|
||||
if (!ipaddr.fromString(buffer)) { return false; }
|
||||
#ifdef ESP32
|
||||
ipaddr.to_ip_addr_t(ip);
|
||||
#else
|
||||
*ip = ipaddr;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniFile::getMACAddress(const char* section, const char* key,
|
||||
uint8_t mac[6])
|
||||
{
|
||||
// Need 18 chars: 6 * 2 hex digits, 5 : or - and a null char
|
||||
if (bufferLen < 18)
|
||||
return false;
|
||||
|
||||
if (!getValue(section, key))
|
||||
return false; // error
|
||||
|
||||
int i = 0;
|
||||
char* cp = buffer;
|
||||
memset(mac, 0, 6);
|
||||
|
||||
while (*cp != '\0' && i < 6) {
|
||||
if (*cp == ':' || *cp == '-') {
|
||||
++i;
|
||||
++cp;
|
||||
continue;
|
||||
}
|
||||
if (isdigit(*cp)) {
|
||||
mac[i] *= 16; // working in hex!
|
||||
mac[i] += (*cp - '0');
|
||||
}
|
||||
else {
|
||||
if (isxdigit(*cp)) {
|
||||
mac[i] *= 16; // working in hex!
|
||||
mac[i] += (toupper(*cp) - 55); // convert A to 0xA, F to 0xF
|
||||
}
|
||||
else {
|
||||
memset(mac, 0, 6);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
++cp;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniFile::getValueBase64(const char* section, const char* key, uint8_t *value, size_t vlen)
|
||||
{
|
||||
if (!getValue(section, key)) return false; // error
|
||||
size_t len = decode_base64_length((unsigned char*)buffer);
|
||||
if (len != vlen) return false;
|
||||
len = decode_base64((unsigned char*)buffer, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniFile::parseCIDR(String& cidr, ip_addr_t *ip, ip_addr_t *mask)
|
||||
{
|
||||
int32_t slash = cidr.indexOf('/');
|
||||
if (slash < 0) { return false; }
|
||||
IPAddress ipaddr;
|
||||
if (!ipaddr.fromString(cidr.substring(0, slash))) { return false; }
|
||||
#ifdef ESP32
|
||||
ipaddr.to_ip_addr_t(ip);
|
||||
#else
|
||||
*ip = ipaddr;
|
||||
#endif
|
||||
|
||||
int32_t prefixLen = cidr.substring(slash + 1).toInt();
|
||||
if (prefixLen < 0 || prefixLen > 32) { return false; }
|
||||
IPAddress maskaddr((prefixLen <= 0) ? 0 : (0xFFFFFFFF >> (32 - prefixLen)));
|
||||
#ifdef ESP32
|
||||
maskaddr.to_ip_addr_t(mask);
|
||||
#else
|
||||
*mask = maskaddr;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IniFile::getCIDR(const char* section, const char* key, ip_addr_t *ip, ip_addr_t *mask)
|
||||
{
|
||||
String cidr;
|
||||
if (!getValueString(section, key, cidr)) return false; // error
|
||||
return parseCIDR(cidr, ip, mask);
|
||||
}
|
||||
|
||||
bool IniFile::getDomainPort(const char* section, const char* key, String &domain, uint16_t &port)
|
||||
{
|
||||
if (!getValueString(section, key, domain)) return false; // error
|
||||
int32_t colon = domain.indexOf(':');
|
||||
if (colon < 0) { return false; }
|
||||
port = domain.substring(colon + 1).toInt();
|
||||
domain = domain.substring(0, colon);
|
||||
return true;
|
||||
}
|
||||
|
||||
// From the file location saved in 'state' look for the next section and read its name.
|
||||
// The name will be in the buffer. Returns false if no section found.
|
||||
bool IniFile::browseSections(IniFileState &state)
|
||||
{
|
||||
error_t err = errorNoError;
|
||||
char *bufptr = &buffer[0];
|
||||
|
||||
do {
|
||||
err = IniFile::readLine(_file, state.readLinePosition);
|
||||
|
||||
if (err != errorNoError) {
|
||||
// end of file or other error
|
||||
_error = err;
|
||||
return false;
|
||||
} else {
|
||||
char *cp = skipWhiteSpace(buffer);
|
||||
|
||||
if (*cp == '[') {
|
||||
// Found a section, read the name
|
||||
++cp;
|
||||
cp = skipWhiteSpace(cp);
|
||||
char *ep = strchr(cp, ']');
|
||||
if (ep != NULL) {
|
||||
*ep = '\0'; // make ] be end of string
|
||||
removeTrailingWhiteSpace(cp);
|
||||
// Copy from cp to buffer, but the strings overlap so strcpy is out
|
||||
while (*cp != '\0')
|
||||
*bufptr++ = *cp++;
|
||||
*bufptr = '\0';
|
||||
_error = errorNoError;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// continue searching
|
||||
} while (err == errorNoError);
|
||||
|
||||
// we should never get here...
|
||||
_error = err;
|
||||
return false;
|
||||
}
|
||||
|
||||
IniFile::error_t IniFile::readLine(File &file, uint32_t &pos)
|
||||
{
|
||||
if (!file)
|
||||
return errorFileNotOpen;
|
||||
|
||||
if (bufferLen < 3)
|
||||
return errorBufferTooSmall;
|
||||
|
||||
if (!file.seek(pos))
|
||||
return errorSeekError;
|
||||
|
||||
size_t bytesRead = file.readBytes(buffer, bufferLen);
|
||||
if (!bytesRead) {
|
||||
buffer[0] = '\0';
|
||||
//return 1; // done
|
||||
return errorEndOfFile;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < bytesRead && i < bufferLen-1; ++i) {
|
||||
// Test for '\n' with optional '\r' too
|
||||
// if (endOfLineTest(i, '\n', '\r')
|
||||
|
||||
if (buffer[i] == '\n' || buffer[i] == '\r') {
|
||||
char match = buffer[i];
|
||||
char otherNewline = (match == '\n' ? '\r' : '\n');
|
||||
// end of line, discard any trailing character of the other sort
|
||||
// of newline
|
||||
buffer[i] = '\0';
|
||||
|
||||
if (buffer[i+1] == otherNewline)
|
||||
++i;
|
||||
pos += (i + 1); // skip past newline(s)
|
||||
//return (i+1 == bytesRead && !file.available());
|
||||
return errorNoError;
|
||||
}
|
||||
}
|
||||
if (!file.available()) {
|
||||
// end of file without a newline
|
||||
buffer[bytesRead] = '\0';
|
||||
// return 1; //done
|
||||
return errorEndOfFile;
|
||||
}
|
||||
|
||||
buffer[bufferLen-1] = '\0'; // terminate the string
|
||||
return errorBufferTooSmall;
|
||||
}
|
||||
|
||||
bool IniFile::isCommentChar(char c)
|
||||
{
|
||||
return (c == ';' || c == '#');
|
||||
}
|
||||
|
||||
char* IniFile::skipWhiteSpace(char* str)
|
||||
{
|
||||
char *cp = str;
|
||||
if (cp)
|
||||
while (isspace(*cp))
|
||||
++cp;
|
||||
return cp;
|
||||
}
|
||||
|
||||
void IniFile::removeTrailingWhiteSpace(char* str)
|
||||
{
|
||||
if (str == nullptr)
|
||||
return;
|
||||
char *cp = str + strlen(str) - 1;
|
||||
while (cp >= str && isspace(*cp))
|
||||
*cp-- = '\0';
|
||||
}
|
||||
|
||||
bool IniFile::findSection(const char* section, IniFileState &state)
|
||||
{
|
||||
if (section == NULL) {
|
||||
_error = errorSectionNotFound;
|
||||
return true;
|
||||
}
|
||||
|
||||
error_t err = IniFile::readLine(_file, state.readLinePosition);
|
||||
|
||||
if (err != errorNoError && err != errorEndOfFile) {
|
||||
// Signal to caller to stop looking and any error value
|
||||
_error = err;
|
||||
return true;
|
||||
}
|
||||
|
||||
char *cp = skipWhiteSpace(buffer);
|
||||
//if (isCommentChar(*cp))
|
||||
//return (done ? errorSectionNotFound : 0);
|
||||
if (isCommentChar(*cp)) {
|
||||
// return (err == errorEndOfFile ? errorSectionNotFound : errorNoError);
|
||||
if (err == errorEndOfFile) {
|
||||
_error = errorSectionNotFound;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false; // Continue searching
|
||||
}
|
||||
|
||||
if (*cp == '[') {
|
||||
// Start of section
|
||||
++cp;
|
||||
cp = skipWhiteSpace(cp);
|
||||
char *ep = strchr(cp, ']');
|
||||
if (ep != NULL) {
|
||||
*ep = '\0'; // make ] be end of string
|
||||
removeTrailingWhiteSpace(cp);
|
||||
if (strcmp(cp, section) == 0) {
|
||||
_error = errorNoError;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Not a valid section line
|
||||
//return (done ? errorSectionNotFound : 0);
|
||||
if (err == errorEndOfFile) {
|
||||
_error = errorSectionNotFound;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// From the current file location look for the matching key. If
|
||||
// section is non-NULL don't look in the next section
|
||||
bool IniFile::findKey(const char* section, const char* key,
|
||||
char** keyptr,
|
||||
IniFileState &state)
|
||||
{
|
||||
if (key == NULL || *key == '\0') {
|
||||
_error = errorKeyNotFound;
|
||||
return true;
|
||||
}
|
||||
|
||||
error_t err = IniFile::readLine(_file, state.readLinePosition);
|
||||
if (err != errorNoError && err != errorEndOfFile) {
|
||||
_error = err;
|
||||
return true;
|
||||
}
|
||||
|
||||
char *cp = skipWhiteSpace(buffer);
|
||||
// if (isCommentChar(*cp))
|
||||
// return (done ? errorKeyNotFound : 0);
|
||||
if (isCommentChar(*cp)) {
|
||||
if (err == errorEndOfFile) {
|
||||
_error = errorKeyNotFound;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false; // Continue searching
|
||||
}
|
||||
|
||||
if (section && *cp == '[') {
|
||||
// Start of a new section
|
||||
_error = errorKeyNotFound;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find '='
|
||||
char *ep = strchr(cp, '=');
|
||||
if (ep != NULL) {
|
||||
*ep = '\0'; // make = be the end of string
|
||||
removeTrailingWhiteSpace(cp);
|
||||
if (strcmp(cp, key) == 0) {
|
||||
*keyptr = ep + 1;
|
||||
_error = errorNoError;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Not the valid key line
|
||||
if (err == errorEndOfFile) {
|
||||
_error = errorKeyNotFound;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
IniFileState::IniFileState()
|
||||
{
|
||||
readLinePosition = 0;
|
||||
getValueState = funcUnset;
|
||||
}
|
117
lib/lib_ssl/IniFile-Tasmota/src/IniFile.h
Normal file
117
lib/lib_ssl/IniFile-Tasmota/src/IniFile.h
Normal file
@ -0,0 +1,117 @@
|
||||
#ifndef _INIFILE_H
|
||||
#define _INIFILE_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include <FS.h>
|
||||
#include "IPAddress.h"
|
||||
|
||||
#define INIFILE_VERSION "1.3.0"
|
||||
|
||||
class IniFileState;
|
||||
|
||||
class IniFile {
|
||||
public:
|
||||
|
||||
enum error_t {
|
||||
errorNoError = 0,
|
||||
errorFileNotFound,
|
||||
errorFileNotOpen,
|
||||
errorBufferTooSmall,
|
||||
errorSeekError,
|
||||
errorSectionNotFound,
|
||||
errorKeyNotFound,
|
||||
errorEndOfFile,
|
||||
errorUnknownError,
|
||||
};
|
||||
|
||||
// Create an IniFile object. It isn't opened until open() is called on it.
|
||||
IniFile(File &file);
|
||||
~IniFile();
|
||||
|
||||
error_t getError(void);
|
||||
void clearError(void);
|
||||
|
||||
// Get value from the file, but split into many short tasks. Return
|
||||
// value: false means continue, true means stop. Call getError() to
|
||||
// find out if any error
|
||||
bool getValue(const char* section, const char* key, IniFileState &state);
|
||||
|
||||
// Get value, as one big task. Return = true means value is present
|
||||
// in buffer
|
||||
bool getValue(const char* section, const char* key);
|
||||
|
||||
// Get the value as a string, storing the result in a new buffer
|
||||
// (not the working buffer)
|
||||
bool getValueStr(const char* section, const char* key, char *value, size_t vlen);
|
||||
bool getValueString(const char* section, const char* key, String &value);
|
||||
|
||||
|
||||
// Get a boolean value
|
||||
bool getValueBool(const char* section, const char* key, bool& b);
|
||||
|
||||
// Get an integer value
|
||||
bool getValueInt(const char* section, const char* key, int32_t& val);
|
||||
|
||||
// Get an uint16_t value
|
||||
bool getValueUInt16(const char* section, const char* key, uint16_t& val);
|
||||
|
||||
// Get a float value
|
||||
bool getValueFloat(const char* section, const char* key, float& val);
|
||||
|
||||
bool getIPAddress(const char* section, const char* key, ip_addr_t *ip);
|
||||
|
||||
bool getMACAddress(const char* section, const char* key,uint8_t mac[6]);
|
||||
|
||||
bool getValueBase64(const char* section, const char* key, uint8_t *value, size_t vlen);
|
||||
|
||||
static bool parseCIDR(String& str, ip_addr_t *ip, ip_addr_t *mask);
|
||||
bool getCIDR(const char* section, const char* key, ip_addr_t *ip, ip_addr_t *mask);
|
||||
|
||||
bool getDomainPort(const char* section, const char* key, String &domain, uint16_t &port);
|
||||
|
||||
// From the file location saved in 'state' look for the next section and read its name.
|
||||
// The name will be in the buffer. Returns false if no section found.
|
||||
bool browseSections(IniFileState &state);
|
||||
|
||||
// Utility function to read a line from a file, make available to all
|
||||
//static int8_t readLine(File &file, char *buffer, size_t len, uint32_t &pos);
|
||||
error_t readLine(File &file, uint32_t &pos);
|
||||
static bool isCommentChar(char c);
|
||||
static char* skipWhiteSpace(char* str);
|
||||
static void removeTrailingWhiteSpace(char* str);
|
||||
|
||||
protected:
|
||||
// True means stop looking, false means not yet found
|
||||
bool findSection(const char* section, IniFileState &state);
|
||||
bool findKey(const char* section, const char* key, char** keyptr, IniFileState &state);
|
||||
|
||||
|
||||
private:
|
||||
String _filename;
|
||||
error_t _error;
|
||||
File _file;
|
||||
static constexpr size_t bufferLen = 80;
|
||||
char buffer[bufferLen];
|
||||
};
|
||||
|
||||
|
||||
class IniFileState {
|
||||
public:
|
||||
IniFileState();
|
||||
|
||||
private:
|
||||
enum {funcUnset = 0,
|
||||
funcFindSection,
|
||||
funcFindKey,
|
||||
};
|
||||
|
||||
uint32_t readLinePosition;
|
||||
uint8_t getValueState;
|
||||
|
||||
friend class IniFile;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
32
lib/lib_ssl/esp_wireguard-Tasmota/LICENSE
Normal file
32
lib/lib_ssl/esp_wireguard-Tasmota/LICENSE
Normal file
@ -0,0 +1,32 @@
|
||||
Copyright (c) 2021 Kenta Ida (fuga@fugafuga.org)
|
||||
Copyright (c) 2022 Tomoyuki Sakurai (y@trombik.org)
|
||||
Copyright (c) 2023-2024 Simone Rossetto (simros85@gmail.com)
|
||||
|
||||
The original license is below:
|
||||
Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
* 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.
|
||||
* Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
its contributors may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
|
||||
Author: Daniel Hope <daniel.hope@smartalock.com>
|
||||
|
55
lib/lib_ssl/esp_wireguard-Tasmota/README.md
Normal file
55
lib/lib_ssl/esp_wireguard-Tasmota/README.md
Normal file
@ -0,0 +1,55 @@
|
||||
# WireGuard implementation for ESPHome
|
||||
|
||||
This is an implementation of [WireGuard®](https://www.wireguard.com/) VPN
|
||||
for [ESPHome](https://esphome.io/), based on
|
||||
[Wireguard Implementation for ESP-IDF](https://github.com/trombik/esp_wireguard)
|
||||
(by [@trombik](https://github.com/trombik)).
|
||||
|
||||
[](https://registry.platformio.org/libraries/droscy/esp_wireguard)
|
||||
|
||||
|
||||
## Usage
|
||||
|
||||
Please refer to the official documentation of [WireGuard Component](https://esphome.io/components/wireguard)
|
||||
in ESPHome website.
|
||||
|
||||
|
||||
## Compatibility
|
||||
|
||||
This code targets only ESPHome and has been tested on the following platforms:
|
||||
|
||||
* ESP32 (with both frameworks)
|
||||
* ESP8266
|
||||
* LibreTiny (with `bk72` microcontrollers only)
|
||||
|
||||
|
||||
## References
|
||||
|
||||
For additional information see:
|
||||
|
||||
* the original feature-request [esphome/feature-requests#1444](https://github.com/esphome/feature-requests/issues/1444)
|
||||
* the first pull-request [esphome/esphome#4256](https://github.com/esphome/esphome/pull/4256)
|
||||
* `esp8266` support [esphome/esphome#6365](https://github.com/esphome/esphome/pull/6365)
|
||||
* LibreTiny support [droscy/esp_wireguard#4](https://github.com/droscy/esp_wireguard/pull/4)
|
||||
|
||||
|
||||
## License
|
||||
|
||||
BSD 3-Clause License (SPDX ID: BSD-3-Clause)
|
||||
|
||||
This project is licensed under [BSD 3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html)
|
||||
except where explicitly written in files themselves or when other license files state differently.
|
||||
|
||||
"WireGuard" and the "WireGuard" logo are registered trademarks of Jason A. Donenfeld.
|
||||
Please see ["WireGuard" Trademark Usage Policy](https://www.wireguard.com/trademark-policy/)
|
||||
for additional information.
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
* Simone Rossetto (simros85@gmail.com)
|
||||
* Tomoyuki Sakurai (y@trombik.org)
|
||||
* Daniel Hope (daniel.hope@smartalock.com)
|
||||
* Kenta Ida (fuga@fugafuga.org)
|
||||
* Matthew Dempsky
|
||||
* D. J. Bernstein
|
167
lib/lib_ssl/esp_wireguard-Tasmota/include/esp_wireguard.h
Normal file
167
lib/lib_ssl/esp_wireguard-Tasmota/include/esp_wireguard.h
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>
|
||||
* Copyright (c) 2023-2024 Simone Rossetto <simros85@gmail.com>
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
*/
|
||||
#if !defined(__ESP_WIREGUARD__H__)
|
||||
#define __ESP_WIREGUARD__H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_wireguard_err.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <time.h>
|
||||
#include <lwip/netif.h>
|
||||
|
||||
#define WG_KEY_LEN (32)
|
||||
#define WG_B64_KEY_LEN (4 * ((WG_KEY_LEN + 2) / 3))
|
||||
|
||||
typedef uint8_t wg_key_t[WG_KEY_LEN];
|
||||
|
||||
typedef struct {
|
||||
/* interface config */
|
||||
wg_key_t private_key2; /**< private key generated by wg genkey. Required. */
|
||||
uint16_t listen_port; /**< a 16-bit port for listening */
|
||||
uint32_t fw_mark; /**< a 32-bit fwmark for outgoing packets */
|
||||
/* peer config */
|
||||
wg_key_t public_key2; /**< public key calculated by wg pubkey from a private key. Required. */
|
||||
wg_key_t preshared_key2; /**< preshared key generated by wg genpsk. */
|
||||
ip_addr_t address2; /**< a local IP address. */
|
||||
ip_addr_t subnet; /**< a subnet mask of the local IP address. */
|
||||
ip_addr_t netmask2; /**< the global subnet for the netif. */
|
||||
const char* endpoint; /**< an endpoint IP address or hostname. */
|
||||
ip_addr_t endpoint_ip; /**< endpoint IP address (internal use, resolved through dns query) */
|
||||
uint16_t port; /**< a port number of remote endpoint. Default is 51820. */
|
||||
uint16_t persistent_keepalive; /**< a seconds interval, between 1 and 65535 inclusive, of how often to send an
|
||||
authenticated empty packet to the peer for the purpose of keeping a stateful
|
||||
firewall or NAT mapping valid persistently. Set zero to disable the feature.
|
||||
Default is zero. */
|
||||
} wireguard_config_t;
|
||||
|
||||
typedef struct {
|
||||
wireguard_config_t* config; /**< a pointer to wireguard config */
|
||||
struct netif* netif; /**< a pointer to configured netif */
|
||||
} wireguard_ctx_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize WireGuard
|
||||
*
|
||||
* Call this function to initialize the context of WireGuard.
|
||||
*
|
||||
* Do not call this function multiple times.
|
||||
*
|
||||
* To connect to other peer, use `esp_wireguard_disconnect()`, and
|
||||
* `esp_wireguard_init()` with a new configuration. To reconnect to
|
||||
* the same peer just use `esp_wireguard_disconnect()` and then
|
||||
* `esp_wireguard_connect()`.
|
||||
*
|
||||
* @param config WireGuard configuration.
|
||||
* @param[out] ctx Context of WireGuard.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Successfully initilized WireGuard interface.
|
||||
* - ESP_ERR_INVALID_ARG: given argument is invalid.
|
||||
* - ESP_ERR_INVALID_STATE: hostname dns resolution cannot start
|
||||
* - ESP_FAIL: Other error.
|
||||
*/
|
||||
esp_err_t esp_wireguard_init(wireguard_config_t *config, wireguard_ctx_t *ctx);
|
||||
|
||||
/**
|
||||
* @brief Create a WireGuard interface and start establishing the connection
|
||||
* to the peer.
|
||||
*
|
||||
* Call this function to start establishing the connection. Note that `ESP_OK`
|
||||
* does not mean the connection is established. To see if the connection is
|
||||
* established, or the peer is up, use `esp_wireguard_peer_is_up()`.
|
||||
*
|
||||
* Do not call this function multiple times.
|
||||
*
|
||||
* @param ctx Context of WireGuard.
|
||||
* @return
|
||||
* - ESP_OK on success.
|
||||
* - ESP_ERR_INVALID_ARG if input arguments are invalid
|
||||
* - ESP_ERR_RETRY dns query still ongoing for endpoint hostname resolution (retry connection)
|
||||
* - ESP_ERR_INVALID_IP if endpoint IP address is missing or invalid (dns query failed)
|
||||
* - ESP_FAIL on failure.
|
||||
*/
|
||||
esp_err_t esp_wireguard_connect(wireguard_ctx_t *ctx);
|
||||
|
||||
/**
|
||||
* @brief Test if the peer is up.
|
||||
* @param ctx Context of WireGuard
|
||||
* @return
|
||||
* - ESP_OK on peer up.
|
||||
* - ESP_ERR_INVALID_ARG if ctx is NULL.
|
||||
* - ESP_FAIL on peer still down.
|
||||
*/
|
||||
esp_err_t esp_wireguard_peer_is_up(const wireguard_ctx_t *ctx);
|
||||
|
||||
/**
|
||||
* @brief Get timestamp of the latest handshake (with seconds resolution since unix epoch)
|
||||
* @param ctx Context of WireGuard
|
||||
* @param result the output timestamp
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL if no handshake already completed
|
||||
* - ESP_ERR_INVALID_ARG if ctx is NULL
|
||||
* - ESP_ERR_INVALID_STATE if data inside ctx is not valid
|
||||
*/
|
||||
esp_err_t esp_wireguard_latest_handshake(const wireguard_ctx_t *ctx, time_t *result);
|
||||
|
||||
/**
|
||||
* @brief Add new allowed IP/mask to the list of allowed ip/mask
|
||||
* @param ctx Context of WireGuard
|
||||
* @param allowed_ip The new IP to be allowed through tunnel
|
||||
* @param allowed_ip_mask The mask of the new IP
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_FAIL if the adding failed
|
||||
* - ESP_ERR_INVALID_ARG if ctx, allowed_ip or allowed_ip_mask are invalid or empty
|
||||
* - ESP_ERR_INVALID_STATE if data inside ctx is not valid
|
||||
*/
|
||||
esp_err_t esp_wireguard_add_allowed_ip(const wireguard_ctx_t *ctx, const ip_addr_t& allowed_ip, const ip_addr_t& allowed_ip_mask);
|
||||
|
||||
/**
|
||||
* @brief Disconnect from the peer
|
||||
*
|
||||
* @param ctx Context of WireGuard.
|
||||
* @return
|
||||
* - ESP_OK on success.
|
||||
*/
|
||||
esp_err_t esp_wireguard_disconnect(wireguard_ctx_t *ctx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
// vim: expandtab tabstop=4
|
55
lib/lib_ssl/esp_wireguard-Tasmota/library.json
Normal file
55
lib/lib_ssl/esp_wireguard-Tasmota/library.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"name": "esp_wireguard_tasmota",
|
||||
"version": "0.4.2",
|
||||
"description": "WireGuard implementation for Tasmota, based on the version for ESPHome",
|
||||
"keywords":[
|
||||
"tasmota",
|
||||
"communication",
|
||||
"network",
|
||||
"wireguard",
|
||||
"vpn"
|
||||
],
|
||||
"authors":[
|
||||
{
|
||||
"name": "Stephan Hadinger",
|
||||
"email": "stephan.hadinger@gmail.com",
|
||||
"url": "https://github.com/arendst/Tasmota",
|
||||
"maintainer": true
|
||||
},
|
||||
{
|
||||
"name": "Simone Rossetto",
|
||||
"email": "simros85@gmail.com",
|
||||
"url": "https://github.com/droscy",
|
||||
"maintainer": true
|
||||
},
|
||||
{
|
||||
"name": "Tomoyuki Sakurai",
|
||||
"email": "y@trombik.org",
|
||||
"url": "https://github.com/trombik"
|
||||
},
|
||||
{
|
||||
"name": "Daniel Hope",
|
||||
"email": "daniel.hope@smartalock.com",
|
||||
"url": "https://github.com/smartalock"
|
||||
},
|
||||
{
|
||||
"name": "Kenta Ida",
|
||||
"email": "fuga@fugafuga.org"
|
||||
},
|
||||
{
|
||||
"name": "Matthew Dempsky"
|
||||
},
|
||||
{
|
||||
"name": "D. J. Bernstein"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause",
|
||||
"platforms": [
|
||||
"espressif32",
|
||||
"espressif8266"
|
||||
],
|
||||
"frameworks":[
|
||||
"espidf",
|
||||
"arduino"
|
||||
]
|
||||
}
|
25
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto.c
Normal file
25
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto.c
Normal file
@ -0,0 +1,25 @@
|
||||
#include "crypto.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
void crypto_zero(void *dest, size_t len) {
|
||||
volatile uint8_t *p = (uint8_t *)dest;
|
||||
while (len--) {
|
||||
*p++ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool crypto_equal(const void *a, const void *b, size_t size) {
|
||||
uint8_t neq = 0;
|
||||
while (size > 0) {
|
||||
neq |= *(uint8_t *)a ^ *(uint8_t *)b;
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wpointer-arith"
|
||||
a += 1;
|
||||
b += 1;
|
||||
#pragma GCC diagnostic pop
|
||||
size -= 1;
|
||||
}
|
||||
return (neq) ? false : true;
|
||||
}
|
106
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto.h
Normal file
106
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto.h
Normal file
@ -0,0 +1,106 @@
|
||||
#ifndef _CRYPTO_H_
|
||||
#define _CRYPTO_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// BLAKE2S IMPLEMENTATION
|
||||
#include "crypto/refc/blake2s.h"
|
||||
#define wireguard_blake2s_ctx blake2s_ctx
|
||||
#define wireguard_blake2s_init(ctx,outlen,key,keylen) blake2s_init(ctx,outlen,key,keylen)
|
||||
#define wireguard_blake2s_update(ctx,in,inlen) blake2s_update(ctx,in,inlen)
|
||||
#define wireguard_blake2s_final(ctx,out) blake2s_final(ctx,out)
|
||||
#define wireguard_blake2s(out,outlen,key,keylen,in,inlen) blake2s(out,outlen,key,keylen,in,inlen)
|
||||
|
||||
// X25519 IMPLEMENTATION
|
||||
int crypto_scalarmult_curve25519(unsigned char *q, const unsigned char *n,const unsigned char *p);
|
||||
#define wireguard_x25519(a,b,c) crypto_scalarmult_curve25519(a,b,c)
|
||||
|
||||
// CHACHA20POLY1305 IMPLEMENTATION
|
||||
#include "crypto/refc/chacha20poly1305.h"
|
||||
#define wireguard_aead_encrypt(dst,src,srclen,ad,adlen,nonce,key) chacha20poly1305_encrypt(dst,src,srclen,ad,adlen,nonce,key)
|
||||
#define wireguard_aead_decrypt(dst,src,srclen,ad,adlen,nonce,key) chacha20poly1305_decrypt(dst,src,srclen,ad,adlen,nonce,key)
|
||||
#define wireguard_xaead_encrypt(dst,src,srclen,ad,adlen,nonce,key) xchacha20poly1305_encrypt(dst,src,srclen,ad,adlen,nonce,key)
|
||||
#define wireguard_xaead_decrypt(dst,src,srclen,ad,adlen,nonce,key) xchacha20poly1305_decrypt(dst,src,srclen,ad,adlen,nonce,key)
|
||||
|
||||
|
||||
// Endian / unaligned helper macros
|
||||
#define U8C(v) (v##U)
|
||||
#define U32C(v) (v##U)
|
||||
|
||||
#define U8V(v) ((uint8_t)(v) & U8C(0xFF))
|
||||
#define U32V(v) ((uint32_t)(v) & U32C(0xFFFFFFFF))
|
||||
|
||||
#define U8TO32_LITTLE(p) \
|
||||
(((uint32_t)((p)[0]) ) | \
|
||||
((uint32_t)((p)[1]) << 8) | \
|
||||
((uint32_t)((p)[2]) << 16) | \
|
||||
((uint32_t)((p)[3]) << 24))
|
||||
|
||||
#define U8TO64_LITTLE(p) \
|
||||
(((uint64_t)((p)[0]) ) | \
|
||||
((uint64_t)((p)[1]) << 8) | \
|
||||
((uint64_t)((p)[2]) << 16) | \
|
||||
((uint64_t)((p)[3]) << 24) | \
|
||||
((uint64_t)((p)[4]) << 32) | \
|
||||
((uint64_t)((p)[5]) << 40) | \
|
||||
((uint64_t)((p)[6]) << 48) | \
|
||||
((uint64_t)((p)[7]) << 56))
|
||||
|
||||
#define U16TO8_BIG(p, v) \
|
||||
do { \
|
||||
(p)[1] = U8V((v) ); \
|
||||
(p)[0] = U8V((v) >> 8); \
|
||||
} while (0)
|
||||
|
||||
#define U32TO8_LITTLE(p, v) \
|
||||
do { \
|
||||
(p)[0] = U8V((v) ); \
|
||||
(p)[1] = U8V((v) >> 8); \
|
||||
(p)[2] = U8V((v) >> 16); \
|
||||
(p)[3] = U8V((v) >> 24); \
|
||||
} while (0)
|
||||
|
||||
#define U32TO8_BIG(p, v) \
|
||||
do { \
|
||||
(p)[3] = U8V((v) ); \
|
||||
(p)[2] = U8V((v) >> 8); \
|
||||
(p)[1] = U8V((v) >> 16); \
|
||||
(p)[0] = U8V((v) >> 24); \
|
||||
} while (0)
|
||||
|
||||
#define U64TO8_LITTLE(p, v) \
|
||||
do { \
|
||||
(p)[0] = U8V((v) ); \
|
||||
(p)[1] = U8V((v) >> 8); \
|
||||
(p)[2] = U8V((v) >> 16); \
|
||||
(p)[3] = U8V((v) >> 24); \
|
||||
(p)[4] = U8V((v) >> 32); \
|
||||
(p)[5] = U8V((v) >> 40); \
|
||||
(p)[6] = U8V((v) >> 48); \
|
||||
(p)[7] = U8V((v) >> 56); \
|
||||
} while (0)
|
||||
|
||||
#define U64TO8_BIG(p, v) \
|
||||
do { \
|
||||
(p)[7] = U8V((v) ); \
|
||||
(p)[6] = U8V((v) >> 8); \
|
||||
(p)[5] = U8V((v) >> 16); \
|
||||
(p)[4] = U8V((v) >> 24); \
|
||||
(p)[3] = U8V((v) >> 32); \
|
||||
(p)[2] = U8V((v) >> 40); \
|
||||
(p)[1] = U8V((v) >> 48); \
|
||||
(p)[0] = U8V((v) >> 56); \
|
||||
} while (0)
|
||||
|
||||
|
||||
void crypto_zero(void *dest, size_t len);
|
||||
bool crypto_equal(const void *a, const void *b, size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _CRYPTO_H_ */
|
||||
|
158
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto/refc/blake2s.cpp
Normal file
158
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto/refc/blake2s.cpp
Normal file
@ -0,0 +1,158 @@
|
||||
// Taken from RFC7693 - https://tools.ietf.org/html/rfc7693
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "blake2s.h"
|
||||
#include "../../crypto.h"
|
||||
|
||||
// Cyclic right rotation.
|
||||
|
||||
#ifndef ROTR32
|
||||
#define ROTR32(x, y) (((x) >> (y)) ^ ((x) << (32 - (y))))
|
||||
#endif
|
||||
|
||||
// Mixing function G.
|
||||
#define B2S_G(a, b, c, d, x, y) { \
|
||||
v[a] = v[a] + v[b] + x; \
|
||||
v[d] = ROTR32(v[d] ^ v[a], 16); \
|
||||
v[c] = v[c] + v[d]; \
|
||||
v[b] = ROTR32(v[b] ^ v[c], 12); \
|
||||
v[a] = v[a] + v[b] + y; \
|
||||
v[d] = ROTR32(v[d] ^ v[a], 8); \
|
||||
v[c] = v[c] + v[d]; \
|
||||
v[b] = ROTR32(v[b] ^ v[c], 7); }
|
||||
|
||||
// Initialization Vector.
|
||||
static const uint32_t blake2s_iv[8] =
|
||||
{
|
||||
0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
|
||||
0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
|
||||
};
|
||||
|
||||
// Compression function. "last" flag indicates last block.
|
||||
static void blake2s_compress(blake2s_ctx *ctx, int last)
|
||||
{
|
||||
const uint8_t sigma[10][16] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 },
|
||||
{ 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 },
|
||||
{ 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 },
|
||||
{ 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 },
|
||||
{ 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 },
|
||||
{ 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 },
|
||||
{ 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 },
|
||||
{ 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 },
|
||||
{ 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13, 0 }
|
||||
};
|
||||
int i;
|
||||
uint32_t v[16], m[16];
|
||||
|
||||
for (i = 0; i < 8; i++) { // init work variables
|
||||
v[i] = ctx->h[i];
|
||||
v[i + 8] = blake2s_iv[i];
|
||||
}
|
||||
|
||||
v[12] ^= ctx->t[0]; // low 32 bits of offset
|
||||
v[13] ^= ctx->t[1]; // high 32 bits
|
||||
if (last) // last block flag set ?
|
||||
v[14] = ~v[14];
|
||||
for (i = 0; i < 16; i++) // get little-endian words
|
||||
m[i] = U8TO32_LITTLE(&ctx->b[4 * i]);
|
||||
|
||||
for (i = 0; i < 10; i++) { // ten rounds
|
||||
B2S_G( 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]);
|
||||
B2S_G( 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]);
|
||||
B2S_G( 2, 6, 10, 14, m[sigma[i][ 4]], m[sigma[i][ 5]]);
|
||||
B2S_G( 3, 7, 11, 15, m[sigma[i][ 6]], m[sigma[i][ 7]]);
|
||||
B2S_G( 0, 5, 10, 15, m[sigma[i][ 8]], m[sigma[i][ 9]]);
|
||||
B2S_G( 1, 6, 11, 12, m[sigma[i][10]], m[sigma[i][11]]);
|
||||
B2S_G( 2, 7, 8, 13, m[sigma[i][12]], m[sigma[i][13]]);
|
||||
B2S_G( 3, 4, 9, 14, m[sigma[i][14]], m[sigma[i][15]]);
|
||||
}
|
||||
|
||||
for( i = 0; i < 8; ++i )
|
||||
ctx->h[i] ^= v[i] ^ v[i + 8];
|
||||
}
|
||||
|
||||
// Initialize the hashing context "ctx" with optional key "key".
|
||||
// 1 <= outlen <= 32 gives the digest size in bytes.
|
||||
// Secret key (also <= 32 bytes) is optional (keylen = 0).
|
||||
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
|
||||
const void *key, size_t keylen) // (keylen=0: no key)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
if (outlen == 0 || outlen > 32 || keylen > 32)
|
||||
return -1; // illegal parameters
|
||||
|
||||
for (i = 0; i < 8; i++) // state, "param block"
|
||||
ctx->h[i] = blake2s_iv[i];
|
||||
ctx->h[0] ^= 0x01010000 ^ (keylen << 8) ^ outlen;
|
||||
|
||||
ctx->t[0] = 0; // input count low word
|
||||
ctx->t[1] = 0; // input count high word
|
||||
ctx->c = 0; // pointer within buffer
|
||||
ctx->outlen = outlen;
|
||||
|
||||
for (i = keylen; i < 64; i++) // zero input block
|
||||
ctx->b[i] = 0;
|
||||
if (keylen > 0) {
|
||||
blake2s_update(ctx, key, keylen);
|
||||
ctx->c = 64; // at the end
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add "inlen" bytes from "in" into the hash.
|
||||
void blake2s_update(blake2s_ctx *ctx,
|
||||
const void *in, size_t inlen) // data bytes
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < inlen; i++) {
|
||||
if (ctx->c == 64) { // buffer full ?
|
||||
ctx->t[0] += ctx->c; // add counters
|
||||
if (ctx->t[0] < ctx->c) // carry overflow ?
|
||||
ctx->t[1]++; // high word
|
||||
blake2s_compress(ctx, 0); // compress (not last)
|
||||
ctx->c = 0; // counter to zero
|
||||
}
|
||||
// ctx->b[ctx->c++] = ((const uint8_t *) in)[i];
|
||||
ctx->b[ctx->c++] = pgm_read_byte(&((const uint8_t *)in)[i]); // PROGMEM compatible
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the message digest (size given in init).
|
||||
// Result placed in "out".
|
||||
void blake2s_final(blake2s_ctx *ctx, void *out)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
ctx->t[0] += ctx->c; // mark last block offset
|
||||
if (ctx->t[0] < ctx->c) // carry overflow
|
||||
ctx->t[1]++; // high word
|
||||
|
||||
while (ctx->c < 64) // fill up with zeros
|
||||
ctx->b[ctx->c++] = 0;
|
||||
blake2s_compress(ctx, 1); // final block flag = 1
|
||||
|
||||
// little endian convert and store
|
||||
for (i = 0; i < ctx->outlen; i++) {
|
||||
((uint8_t *) out)[i] =
|
||||
(ctx->h[i >> 2] >> (8 * (i & 3))) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience function for all-in-one computation.
|
||||
int blake2s(void *out, size_t outlen,
|
||||
const void *key, size_t keylen,
|
||||
const void *in, size_t inlen)
|
||||
{
|
||||
blake2s_ctx ctx;
|
||||
if (blake2s_init(&ctx, outlen, key, keylen))
|
||||
return -1;
|
||||
blake2s_update(&ctx, in, inlen);
|
||||
blake2s_final(&ctx, out);
|
||||
|
||||
return 0;
|
||||
}
|
47
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto/refc/blake2s.h
Normal file
47
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto/refc/blake2s.h
Normal file
@ -0,0 +1,47 @@
|
||||
// Taken from RFC7693 - https://tools.ietf.org/html/rfc7693
|
||||
// BLAKE2s Hashing Context and API Prototypes
|
||||
#ifndef _BLAKE2S_H
|
||||
#define _BLAKE2S_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BLAKE2S_BLOCK_SIZE 64
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
// state context
|
||||
typedef struct {
|
||||
uint8_t b[64]; // input buffer
|
||||
uint32_t h[8]; // chained state
|
||||
uint32_t t[2]; // total number of bytes
|
||||
size_t c; // pointer for b[]
|
||||
size_t outlen; // digest size
|
||||
} blake2s_ctx;
|
||||
|
||||
// Initialize the hashing context "ctx" with optional key "key".
|
||||
// 1 <= outlen <= 32 gives the digest size in bytes.
|
||||
// Secret key (also <= 32 bytes) is optional (keylen = 0).
|
||||
int blake2s_init(blake2s_ctx *ctx, size_t outlen,
|
||||
const void *key, size_t keylen); // secret key
|
||||
|
||||
// Add "inlen" bytes from "in" into the hash.
|
||||
void blake2s_update(blake2s_ctx *ctx, // context
|
||||
const void *in, size_t inlen); // data to be hashed
|
||||
|
||||
// Generate the message digest (size given in init).
|
||||
// Result placed in "out".
|
||||
void blake2s_final(blake2s_ctx *ctx, void *out);
|
||||
|
||||
// All-in-one convenience function.
|
||||
int blake2s(void *out, size_t outlen, // return buffer for digest
|
||||
const void *key, size_t keylen, // optional secret key
|
||||
const void *in, size_t inlen); // data to be hashed
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
202
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto/refc/chacha20.cpp
Normal file
202
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto/refc/chacha20.cpp
Normal file
@ -0,0 +1,202 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
* Author: Daniel Hope <daniel.hope@smartalock.com>
|
||||
*/
|
||||
|
||||
// RFC7539 implementation of ChaCha20 with modified nonce size for WireGuard
|
||||
// https://tools.ietf.org/html/rfc7539
|
||||
// Adapted from https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/chacha.c by D. J. Bernstein (Public Domain)
|
||||
// HChaCha20 is described here: https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html
|
||||
|
||||
#include "chacha20.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include "../../crypto.h"
|
||||
|
||||
// 2.3. The ChaCha20 Block Function
|
||||
// The first four words (0-3) are constants: 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
|
||||
static const uint32_t CHACHA20_CONSTANT_1 = 0x61707865;
|
||||
static const uint32_t CHACHA20_CONSTANT_2 = 0x3320646e;
|
||||
static const uint32_t CHACHA20_CONSTANT_3 = 0x79622d32;
|
||||
static const uint32_t CHACHA20_CONSTANT_4 = 0x6b206574;
|
||||
|
||||
#define ROTL32(v, n) (U32V((v) << (n)) | ((v) >> (32 - (n))))
|
||||
|
||||
#define PLUS(v,w) (U32V((v) + (w)))
|
||||
#define PLUSONE(v) (PLUS((v),1))
|
||||
|
||||
// 2.1. The ChaCha Quarter Round
|
||||
// 1. a += b; d ^= a; d <<<= 16;
|
||||
// 2. c += d; b ^= c; b <<<= 12;
|
||||
// 3. a += b; d ^= a; d <<<= 8;
|
||||
// 4. c += d; b ^= c; b <<<= 7;
|
||||
|
||||
#define QUARTERROUND(a, b, c, d) \
|
||||
a += b; d ^= a; d = ROTL32(d, 16); \
|
||||
c += d; b ^= c; b = ROTL32(b, 12); \
|
||||
a += b; d ^= a; d = ROTL32(d, 8); \
|
||||
c += d; b ^= c; b = ROTL32(b, 7)
|
||||
|
||||
static inline void INNER_BLOCK(uint32_t *block) {
|
||||
QUARTERROUND(block[0], block[4], block[ 8], block[12]); // column 0
|
||||
QUARTERROUND(block[1], block[5], block[ 9], block[13]); // column 1
|
||||
QUARTERROUND(block[2], block[6], block[10], block[14]); // column 2
|
||||
QUARTERROUND(block[3], block[7], block[11], block[15]); // column 3
|
||||
QUARTERROUND(block[0], block[5], block[10], block[15]); // diagonal 1
|
||||
QUARTERROUND(block[1], block[6], block[11], block[12]); // diagonal 2
|
||||
QUARTERROUND(block[2], block[7], block[ 8], block[13]); // diagonal 3
|
||||
QUARTERROUND(block[3], block[4], block[ 9], block[14]); // diagonal 4
|
||||
}
|
||||
|
||||
#define TWENTY_ROUNDS(x) ( \
|
||||
INNER_BLOCK(x), \
|
||||
INNER_BLOCK(x), \
|
||||
INNER_BLOCK(x), \
|
||||
INNER_BLOCK(x), \
|
||||
INNER_BLOCK(x), \
|
||||
INNER_BLOCK(x), \
|
||||
INNER_BLOCK(x), \
|
||||
INNER_BLOCK(x), \
|
||||
INNER_BLOCK(x), \
|
||||
INNER_BLOCK(x) \
|
||||
)
|
||||
|
||||
// 2.3. The ChaCha20 Block Function
|
||||
// chacha20_block(key, counter, nonce):
|
||||
// state = constants | key | counter | nonce
|
||||
// working_state = state
|
||||
// for i=1 upto 10
|
||||
// inner_block(working_state)
|
||||
// end
|
||||
// state += working_state
|
||||
// return serialize(state)
|
||||
// end
|
||||
static void chacha20_block(struct chacha20_ctx *ctx, uint8_t *stream) {
|
||||
uint32_t working_state[16];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
working_state[i] = ctx->state[i];
|
||||
}
|
||||
|
||||
TWENTY_ROUNDS(working_state);
|
||||
|
||||
for (i = 0; i < 16; ++i) {
|
||||
U32TO8_LITTLE(stream + (4 * i), PLUS(working_state[i], ctx->state[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void chacha20(struct chacha20_ctx *ctx, uint8_t *out, const uint8_t *in, uint32_t len) {
|
||||
uint8_t output[CHACHA20_BLOCK_SIZE];
|
||||
int i;
|
||||
|
||||
if (len) {
|
||||
for (;;) {
|
||||
chacha20_block(ctx, output);
|
||||
// Word 12 is a block counter
|
||||
ctx->state[12] = PLUSONE(ctx->state[12]);
|
||||
if (len <= 64) {
|
||||
for (i = 0;i < len;++i) {
|
||||
out[i] = in[i] ^ output[i];
|
||||
}
|
||||
return;
|
||||
}
|
||||
for (i = 0;i < 64;++i) {
|
||||
out[i] = in[i] ^ output[i];
|
||||
}
|
||||
len -= 64;
|
||||
out += 64;
|
||||
in += 64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 2.3. The ChaCha20 Block Function
|
||||
// The first four words (0-3) are constants: 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574
|
||||
// The next eight words (4-11) are taken from the 256-bit key by reading the bytes in little-endian order, in 4-byte chunks.
|
||||
// Word 12 is a block counter. Since each block is 64-byte, a 32-bit word is enough for 256 gigabytes of data.
|
||||
// Words 13-15 are a nonce, which should not be repeated for the same key.
|
||||
// For wireguard: "nonce being composed of 32 bits of zeros followed by the 64-bit little-endian value of counter." where counter comes from the Wireguard layer and is separate from the block counter in word 12
|
||||
void chacha20_init(struct chacha20_ctx *ctx, const uint8_t *key, const uint64_t nonce) {
|
||||
ctx->state[0] = CHACHA20_CONSTANT_1;
|
||||
ctx->state[1] = CHACHA20_CONSTANT_2;
|
||||
ctx->state[2] = CHACHA20_CONSTANT_3;
|
||||
ctx->state[3] = CHACHA20_CONSTANT_4;
|
||||
ctx->state[4] = U8TO32_LITTLE(key + 0);
|
||||
ctx->state[5] = U8TO32_LITTLE(key + 4);
|
||||
ctx->state[6] = U8TO32_LITTLE(key + 8);
|
||||
ctx->state[7] = U8TO32_LITTLE(key + 12);
|
||||
ctx->state[8] = U8TO32_LITTLE(key + 16);
|
||||
ctx->state[9] = U8TO32_LITTLE(key + 20);
|
||||
ctx->state[10] = U8TO32_LITTLE(key + 24);
|
||||
ctx->state[11] = U8TO32_LITTLE(key + 28);
|
||||
ctx->state[12] = 0;
|
||||
ctx->state[13] = 0;
|
||||
ctx->state[14] = nonce & 0xFFFFFFFF;
|
||||
ctx->state[15] = nonce >> 32;
|
||||
}
|
||||
|
||||
// 2.2. HChaCha20
|
||||
// HChaCha20 is initialized the same way as the ChaCha cipher, except that HChaCha20 uses a 128-bit nonce and has no counter.
|
||||
// After initialization, proceed through the ChaCha rounds as usual.
|
||||
// Once the 20 ChaCha rounds have been completed, the first 128 bits and last 128 bits of the ChaCha state (both little-endian) are concatenated, and this 256-bit subkey is returned.
|
||||
void hchacha20(uint8_t *out, const uint8_t *nonce, const uint8_t *key) {
|
||||
uint32_t state[16];
|
||||
state[0] = CHACHA20_CONSTANT_1;
|
||||
state[1] = CHACHA20_CONSTANT_2;
|
||||
state[2] = CHACHA20_CONSTANT_3;
|
||||
state[3] = CHACHA20_CONSTANT_4;
|
||||
state[4] = U8TO32_LITTLE(key + 0);
|
||||
state[5] = U8TO32_LITTLE(key + 4);
|
||||
state[6] = U8TO32_LITTLE(key + 8);
|
||||
state[7] = U8TO32_LITTLE(key + 12);
|
||||
state[8] = U8TO32_LITTLE(key + 16);
|
||||
state[9] = U8TO32_LITTLE(key + 20);
|
||||
state[10] = U8TO32_LITTLE(key + 24);
|
||||
state[11] = U8TO32_LITTLE(key + 28);
|
||||
state[12] = U8TO32_LITTLE(nonce + 0);
|
||||
state[13] = U8TO32_LITTLE(nonce + 4);
|
||||
state[14] = U8TO32_LITTLE(nonce + 8);
|
||||
state[15] = U8TO32_LITTLE(nonce + 12);
|
||||
|
||||
TWENTY_ROUNDS(state);
|
||||
|
||||
// Concatenate first/last 128 bits into 256bit output (as little endian)
|
||||
U32TO8_LITTLE(out + 0, state[0]);
|
||||
U32TO8_LITTLE(out + 4, state[1]);
|
||||
U32TO8_LITTLE(out + 8, state[2]);
|
||||
U32TO8_LITTLE(out + 12, state[3]);
|
||||
U32TO8_LITTLE(out + 16, state[12]);
|
||||
U32TO8_LITTLE(out + 20, state[13]);
|
||||
U32TO8_LITTLE(out + 24, state[14]);
|
||||
U32TO8_LITTLE(out + 28, state[15]);
|
||||
}
|
61
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto/refc/chacha20.h
Normal file
61
lib/lib_ssl/esp_wireguard-Tasmota/src/crypto/refc/chacha20.h
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
* Author: Daniel Hope <daniel.hope@smartalock.com>
|
||||
*/
|
||||
|
||||
// RFC7539 implementation of ChaCha20 with modified nonce size for WireGuard
|
||||
// https://tools.ietf.org/html/rfc7539
|
||||
// Adapted from https://cr.yp.to/streamciphers/timings/estreambench/submissions/salsa20/chacha8/ref/chacha.c by D. J. Bernstein (Public Domain)
|
||||
// HChaCha20 is described here: https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html
|
||||
#ifndef _CHACHA20_H_
|
||||
#define _CHACHA20_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CHACHA20_BLOCK_SIZE (64)
|
||||
#define CHACHA20_KEY_SIZE (32)
|
||||
|
||||
struct chacha20_ctx {
|
||||
uint32_t state[16];
|
||||
};
|
||||
|
||||
void chacha20_init(struct chacha20_ctx *ctx, const uint8_t *key, const uint64_t nonce);
|
||||
void chacha20(struct chacha20_ctx *ctx, uint8_t *out, const uint8_t *in, uint32_t len);
|
||||
void hchacha20(uint8_t *out, const uint8_t *nonce, const uint8_t *key);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _CHACHA20_H_ */
|
@ -0,0 +1,193 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
* Author: Daniel Hope <daniel.hope@smartalock.com>
|
||||
*/
|
||||
|
||||
// AEAD_CHACHA20_POLY1305 as described in https://tools.ietf.org/html/rfc7539
|
||||
// AEAD_XChaCha20_Poly1305 as described in https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html
|
||||
#include "chacha20poly1305.h"
|
||||
#include "chacha20.h"
|
||||
#include "poly1305-donna.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include "../../crypto.h"
|
||||
|
||||
#define POLY1305_KEY_SIZE 32
|
||||
#define POLY1305_MAC_SIZE 16
|
||||
|
||||
static const uint8_t zero[CHACHA20_BLOCK_SIZE] = { 0 };
|
||||
|
||||
// 2.6. Generating the Poly1305 Key Using ChaCha20
|
||||
static void generate_poly1305_key(struct poly1305_context *poly1305_state, struct chacha20_ctx *chacha20_state, const uint8_t *key, uint64_t nonce) {
|
||||
uint8_t block[POLY1305_KEY_SIZE] = {0};
|
||||
|
||||
// The method is to call the block function with the following parameters:
|
||||
// - The 256-bit session integrity key is used as the ChaCha20 key.
|
||||
// - The block counter is set to zero.
|
||||
// - The protocol will specify a 96-bit or 64-bit nonce
|
||||
chacha20_init(chacha20_state, key, nonce);
|
||||
|
||||
// We take the first 256 bits or the serialized state, and use those as the one-time Poly1305 key
|
||||
chacha20(chacha20_state, block, block, sizeof(block));
|
||||
|
||||
poly1305_init(poly1305_state, block);
|
||||
|
||||
crypto_zero(&block, sizeof(block));
|
||||
}
|
||||
|
||||
// 2.8. AEAD Construction (Encryption)
|
||||
void chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, uint64_t nonce, const uint8_t *key) {
|
||||
struct poly1305_context poly1305_state;
|
||||
struct chacha20_ctx chacha20_state;
|
||||
uint8_t block[8];
|
||||
size_t padded_len;
|
||||
|
||||
// First, a Poly1305 one-time key is generated from the 256-bit key and nonce using the procedure described in Section 2.6.
|
||||
generate_poly1305_key(&poly1305_state, &chacha20_state, key, nonce);
|
||||
|
||||
// Next, the ChaCha20 encryption function is called to encrypt the plaintext, using the same key and nonce, and with the initial counter set to 1.
|
||||
chacha20(&chacha20_state, dst, src, src_len);
|
||||
|
||||
// Finally, the Poly1305 function is called with the Poly1305 key calculated above, and a message constructed as a concatenation of the following:
|
||||
// - The AAD
|
||||
poly1305_update(&poly1305_state, ad, ad_len);
|
||||
// - padding1 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16
|
||||
padded_len = (ad_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes
|
||||
poly1305_update(&poly1305_state, zero, padded_len - ad_len);
|
||||
// - The ciphertext
|
||||
poly1305_update(&poly1305_state, dst, src_len);
|
||||
// - padding2 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16.
|
||||
padded_len = (src_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes
|
||||
poly1305_update(&poly1305_state, zero, padded_len - src_len);
|
||||
// - The length of the additional data in octets (as a 64-bit little-endian integer)
|
||||
U64TO8_LITTLE(block, (uint64_t)ad_len);
|
||||
poly1305_update(&poly1305_state, block, sizeof(block));
|
||||
// - The length of the ciphertext in octets (as a 64-bit little-endian integer).
|
||||
U64TO8_LITTLE(block, (uint64_t)src_len);
|
||||
poly1305_update(&poly1305_state, block, sizeof(block));
|
||||
|
||||
// The output from the AEAD is twofold:
|
||||
// - A ciphertext of the same length as the plaintext. (above, output of chacha20 into dst)
|
||||
// - A 128-bit tag, which is the output of the Poly1305 function. (append to dst)
|
||||
poly1305_finish(&poly1305_state, dst + src_len);
|
||||
|
||||
// Make sure we leave nothing sensitive on the stack
|
||||
crypto_zero(&chacha20_state, sizeof(chacha20_state));
|
||||
crypto_zero(&block, sizeof(block));
|
||||
}
|
||||
|
||||
// 2.8. AEAD Construction (Decryption)
|
||||
bool chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, uint64_t nonce, const uint8_t *key) {
|
||||
struct poly1305_context poly1305_state;
|
||||
struct chacha20_ctx chacha20_state;
|
||||
uint8_t block[8];
|
||||
uint8_t mac[POLY1305_MAC_SIZE];
|
||||
size_t padded_len;
|
||||
int dst_len;
|
||||
bool result = false;
|
||||
|
||||
// Decryption is similar [to encryption] with the following differences:
|
||||
// - The roles of ciphertext and plaintext are reversed, so the ChaCha20 encryption function is applied to the ciphertext, producing the plaintext.
|
||||
// - The Poly1305 function is still run on the AAD and the ciphertext, not the plaintext.
|
||||
// - The calculated tag is bitwise compared to the received tag. The message is authenticated if and only if the tags match.
|
||||
|
||||
if (src_len >= POLY1305_MAC_SIZE) {
|
||||
dst_len = src_len - POLY1305_MAC_SIZE;
|
||||
|
||||
// First, a Poly1305 one-time key is generated from the 256-bit key and nonce using the procedure described in Section 2.6.
|
||||
generate_poly1305_key(&poly1305_state, &chacha20_state, key, nonce);
|
||||
|
||||
// Calculate the MAC before attempting decryption
|
||||
|
||||
// the Poly1305 function is called with the Poly1305 key calculated above, and a message constructed as a concatenation of the following:
|
||||
// - The AAD
|
||||
poly1305_update(&poly1305_state, ad, ad_len);
|
||||
// - padding1 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16
|
||||
padded_len = (ad_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes
|
||||
poly1305_update(&poly1305_state, zero, padded_len - ad_len);
|
||||
// - The ciphertext (note the Poly1305 function is still run on the AAD and the ciphertext, not the plaintext)
|
||||
poly1305_update(&poly1305_state, src, dst_len);
|
||||
// - padding2 -- the padding is up to 15 zero bytes, and it brings the total length so far to an integral multiple of 16.
|
||||
padded_len = (dst_len + 15) & 0xFFFFFFF0; // Round up to next 16 bytes
|
||||
poly1305_update(&poly1305_state, zero, padded_len - dst_len);
|
||||
// - The length of the additional data in octets (as a 64-bit little-endian integer)
|
||||
U64TO8_LITTLE(block, (uint64_t)ad_len);
|
||||
poly1305_update(&poly1305_state, block, sizeof(block));
|
||||
// - The length of the ciphertext in octets (as a 64-bit little-endian integer).
|
||||
U64TO8_LITTLE(block, (uint64_t)dst_len);
|
||||
poly1305_update(&poly1305_state, block, sizeof(block));
|
||||
|
||||
// The output from the AEAD is twofold:
|
||||
// - A plaintext of the same length as the ciphertext. (below, output of chacha20 into dst)
|
||||
// - A 128-bit tag, which is the output of the Poly1305 function. (into mac for checking against passed mac)
|
||||
poly1305_finish(&poly1305_state, mac);
|
||||
|
||||
|
||||
if (crypto_equal(mac, src + dst_len, POLY1305_MAC_SIZE)) {
|
||||
// mac is correct - do the decryption
|
||||
// Next, the ChaCha20 encryption function is called to decrypt the ciphertext, using the same key and nonce, and with the initial counter set to 1.
|
||||
chacha20(&chacha20_state, dst, src, dst_len);
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// AEAD_XChaCha20_Poly1305
|
||||
// XChaCha20-Poly1305 is a variant of the ChaCha20-Poly1305 AEAD construction as defined in [RFC7539] that uses a 192-bit nonce instead of a 96-bit nonce.
|
||||
// The algorithm for XChaCha20-Poly1305 is as follows:
|
||||
// 1. Calculate a subkey from the first 16 bytes of the nonce and the key, using HChaCha20 (Section 2.2).
|
||||
// 2. Use the subkey and remaining 8 bytes of the nonce (prefixed with 4 NUL bytes) with AEAD_CHACHA20_POLY1305 from [RFC7539] as normal. The definition for XChaCha20 is given in Section 2.3.
|
||||
void xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, const uint8_t *nonce, const uint8_t *key) {
|
||||
uint8_t subkey[CHACHA20_KEY_SIZE];
|
||||
uint64_t new_nonce;
|
||||
|
||||
new_nonce = U8TO64_LITTLE(nonce + 16);
|
||||
|
||||
hchacha20(subkey, nonce, key);
|
||||
chacha20poly1305_encrypt(dst, src, src_len, ad, ad_len, new_nonce, subkey);
|
||||
|
||||
crypto_zero(subkey, sizeof(subkey));
|
||||
}
|
||||
|
||||
bool xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, const uint8_t *nonce, const uint8_t *key) {
|
||||
uint8_t subkey[CHACHA20_KEY_SIZE];
|
||||
uint64_t new_nonce;
|
||||
bool result;
|
||||
|
||||
new_nonce = U8TO64_LITTLE(nonce + 16);
|
||||
|
||||
hchacha20(subkey, nonce, key);
|
||||
result = chacha20poly1305_decrypt(dst, src, src_len, ad, ad_len, new_nonce, subkey);
|
||||
|
||||
crypto_zero(subkey, sizeof(subkey));
|
||||
return result;
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
* Author: Daniel Hope <daniel.hope@smartalock.com>
|
||||
*/
|
||||
|
||||
#ifndef _CHACHA20POLY1305_H_
|
||||
#define _CHACHA20POLY1305_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
// Aead(key, counter, plain text, auth text) ChaCha20Poly1305 AEAD, as specified in RFC7539 [17], with its nonce being composed of 32 bits of zeros followed by the 64-bit little-endian value of counter.
|
||||
// AEAD_CHACHA20_POLY1305 as described in https://tools.ietf.org/html/rfc7539
|
||||
void chacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, uint64_t nonce, const uint8_t *key);
|
||||
bool chacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, uint64_t nonce, const uint8_t *key);
|
||||
|
||||
// Xaead(key, nonce, plain text, auth text) XChaCha20Poly1305 AEAD, with a 24-byte random nonce, instantiated using HChaCha20 [6] and ChaCha20Poly1305.
|
||||
// AEAD_XChaCha20_Poly1305 as described in https://tools.ietf.org/id/draft-arciszewski-xchacha-02.html
|
||||
void xchacha20poly1305_encrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, const uint8_t *nonce, const uint8_t *key);
|
||||
bool xchacha20poly1305_decrypt(uint8_t *dst, const uint8_t *src, size_t src_len, const uint8_t *ad, size_t ad_len, const uint8_t *nonce, const uint8_t *key);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _CHACHA20POLY1305_H_ */
|
@ -0,0 +1,227 @@
|
||||
// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT
|
||||
/*
|
||||
poly1305 implementation using 32 bit * 32 bit = 64 bit multiplication and 64 bit addition
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#define POLY1305_NOINLINE __declspec(noinline)
|
||||
#elif defined(__GNUC__)
|
||||
#define POLY1305_NOINLINE __attribute__((noinline))
|
||||
#else
|
||||
#define POLY1305_NOINLINE
|
||||
#endif
|
||||
|
||||
#define poly1305_block_size 16
|
||||
|
||||
/* 17 + sizeof(size_t) + 14*sizeof(unsigned long) */
|
||||
typedef struct poly1305_state_internal_t {
|
||||
unsigned long r[5];
|
||||
unsigned long h[5];
|
||||
unsigned long pad[4];
|
||||
size_t leftover;
|
||||
unsigned char buffer[poly1305_block_size];
|
||||
unsigned char final;
|
||||
} poly1305_state_internal_t;
|
||||
|
||||
/* interpret four 8 bit unsigned integers as a 32 bit unsigned integer in little endian */
|
||||
static unsigned long
|
||||
U8TO32(const unsigned char *p) {
|
||||
return
|
||||
(((unsigned long)(p[0] & 0xff) ) |
|
||||
((unsigned long)(p[1] & 0xff) << 8) |
|
||||
((unsigned long)(p[2] & 0xff) << 16) |
|
||||
((unsigned long)(p[3] & 0xff) << 24));
|
||||
}
|
||||
|
||||
/* store a 32 bit unsigned integer as four 8 bit unsigned integers in little endian */
|
||||
static void
|
||||
U32TO8(unsigned char *p, unsigned long v) {
|
||||
p[0] = (v ) & 0xff;
|
||||
p[1] = (v >> 8) & 0xff;
|
||||
p[2] = (v >> 16) & 0xff;
|
||||
p[3] = (v >> 24) & 0xff;
|
||||
}
|
||||
|
||||
void
|
||||
poly1305_init(poly1305_context *ctx, const unsigned char key[32]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
|
||||
/* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
|
||||
st->r[0] = (U8TO32(&key[ 0]) ) & 0x3ffffff;
|
||||
st->r[1] = (U8TO32(&key[ 3]) >> 2) & 0x3ffff03;
|
||||
st->r[2] = (U8TO32(&key[ 6]) >> 4) & 0x3ffc0ff;
|
||||
st->r[3] = (U8TO32(&key[ 9]) >> 6) & 0x3f03fff;
|
||||
st->r[4] = (U8TO32(&key[12]) >> 8) & 0x00fffff;
|
||||
|
||||
/* h = 0 */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
st->h[3] = 0;
|
||||
st->h[4] = 0;
|
||||
|
||||
/* save pad for later */
|
||||
st->pad[0] = U8TO32(&key[16]);
|
||||
st->pad[1] = U8TO32(&key[20]);
|
||||
st->pad[2] = U8TO32(&key[24]);
|
||||
st->pad[3] = U8TO32(&key[28]);
|
||||
|
||||
st->leftover = 0;
|
||||
st->final = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
poly1305_blocks(poly1305_state_internal_t *st, const unsigned char *m, size_t bytes) {
|
||||
const unsigned long hibit = (st->final) ? 0 : (1UL << 24); /* 1 << 128 */
|
||||
unsigned long r0,r1,r2,r3,r4;
|
||||
unsigned long s1,s2,s3,s4;
|
||||
unsigned long h0,h1,h2,h3,h4;
|
||||
unsigned long long d0,d1,d2,d3,d4;
|
||||
unsigned long c;
|
||||
|
||||
r0 = st->r[0];
|
||||
r1 = st->r[1];
|
||||
r2 = st->r[2];
|
||||
r3 = st->r[3];
|
||||
r4 = st->r[4];
|
||||
|
||||
s1 = r1 * 5;
|
||||
s2 = r2 * 5;
|
||||
s3 = r3 * 5;
|
||||
s4 = r4 * 5;
|
||||
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
h3 = st->h[3];
|
||||
h4 = st->h[4];
|
||||
|
||||
while (bytes >= poly1305_block_size) {
|
||||
/* h += m[i] */
|
||||
h0 += (U8TO32(m+ 0) ) & 0x3ffffff;
|
||||
h1 += (U8TO32(m+ 3) >> 2) & 0x3ffffff;
|
||||
h2 += (U8TO32(m+ 6) >> 4) & 0x3ffffff;
|
||||
h3 += (U8TO32(m+ 9) >> 6) & 0x3ffffff;
|
||||
h4 += (U8TO32(m+12) >> 8) | hibit;
|
||||
|
||||
/* h *= r */
|
||||
d0 = ((unsigned long long)h0 * r0) + ((unsigned long long)h1 * s4) + ((unsigned long long)h2 * s3) + ((unsigned long long)h3 * s2) + ((unsigned long long)h4 * s1);
|
||||
d1 = ((unsigned long long)h0 * r1) + ((unsigned long long)h1 * r0) + ((unsigned long long)h2 * s4) + ((unsigned long long)h3 * s3) + ((unsigned long long)h4 * s2);
|
||||
d2 = ((unsigned long long)h0 * r2) + ((unsigned long long)h1 * r1) + ((unsigned long long)h2 * r0) + ((unsigned long long)h3 * s4) + ((unsigned long long)h4 * s3);
|
||||
d3 = ((unsigned long long)h0 * r3) + ((unsigned long long)h1 * r2) + ((unsigned long long)h2 * r1) + ((unsigned long long)h3 * r0) + ((unsigned long long)h4 * s4);
|
||||
d4 = ((unsigned long long)h0 * r4) + ((unsigned long long)h1 * r3) + ((unsigned long long)h2 * r2) + ((unsigned long long)h3 * r1) + ((unsigned long long)h4 * r0);
|
||||
|
||||
/* (partial) h %= p */
|
||||
c = (unsigned long)(d0 >> 26); h0 = (unsigned long)d0 & 0x3ffffff;
|
||||
d1 += c; c = (unsigned long)(d1 >> 26); h1 = (unsigned long)d1 & 0x3ffffff;
|
||||
d2 += c; c = (unsigned long)(d2 >> 26); h2 = (unsigned long)d2 & 0x3ffffff;
|
||||
d3 += c; c = (unsigned long)(d3 >> 26); h3 = (unsigned long)d3 & 0x3ffffff;
|
||||
d4 += c; c = (unsigned long)(d4 >> 26); h4 = (unsigned long)d4 & 0x3ffffff;
|
||||
h0 += c * 5; c = (h0 >> 26); h0 = h0 & 0x3ffffff;
|
||||
h1 += c;
|
||||
|
||||
m += poly1305_block_size;
|
||||
bytes -= poly1305_block_size;
|
||||
}
|
||||
|
||||
st->h[0] = h0;
|
||||
st->h[1] = h1;
|
||||
st->h[2] = h2;
|
||||
st->h[3] = h3;
|
||||
st->h[4] = h4;
|
||||
}
|
||||
|
||||
POLY1305_NOINLINE void
|
||||
poly1305_finish(poly1305_context *ctx, unsigned char mac[16]) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
unsigned long h0,h1,h2,h3,h4,c;
|
||||
unsigned long g0,g1,g2,g3,g4;
|
||||
unsigned long long f;
|
||||
unsigned long mask;
|
||||
|
||||
/* process the remaining block */
|
||||
if (st->leftover) {
|
||||
size_t i = st->leftover;
|
||||
st->buffer[i++] = 1;
|
||||
for (; i < poly1305_block_size; i++)
|
||||
st->buffer[i] = 0;
|
||||
st->final = 1;
|
||||
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
||||
}
|
||||
|
||||
/* fully carry h */
|
||||
h0 = st->h[0];
|
||||
h1 = st->h[1];
|
||||
h2 = st->h[2];
|
||||
h3 = st->h[3];
|
||||
h4 = st->h[4];
|
||||
|
||||
c = h1 >> 26; h1 = h1 & 0x3ffffff;
|
||||
h2 += c; c = h2 >> 26; h2 = h2 & 0x3ffffff;
|
||||
h3 += c; c = h3 >> 26; h3 = h3 & 0x3ffffff;
|
||||
h4 += c; c = h4 >> 26; h4 = h4 & 0x3ffffff;
|
||||
h0 += c * 5; c = h0 >> 26; h0 = h0 & 0x3ffffff;
|
||||
h1 += c;
|
||||
|
||||
/* compute h + -p */
|
||||
g0 = h0 + 5; c = g0 >> 26; g0 &= 0x3ffffff;
|
||||
g1 = h1 + c; c = g1 >> 26; g1 &= 0x3ffffff;
|
||||
g2 = h2 + c; c = g2 >> 26; g2 &= 0x3ffffff;
|
||||
g3 = h3 + c; c = g3 >> 26; g3 &= 0x3ffffff;
|
||||
g4 = h4 + c - (1UL << 26);
|
||||
|
||||
/* select h if h < p, or h + -p if h >= p */
|
||||
mask = (g4 >> ((sizeof(unsigned long) * 8) - 1)) - 1;
|
||||
g0 &= mask;
|
||||
g1 &= mask;
|
||||
g2 &= mask;
|
||||
g3 &= mask;
|
||||
g4 &= mask;
|
||||
mask = ~mask;
|
||||
h0 = (h0 & mask) | g0;
|
||||
h1 = (h1 & mask) | g1;
|
||||
h2 = (h2 & mask) | g2;
|
||||
h3 = (h3 & mask) | g3;
|
||||
h4 = (h4 & mask) | g4;
|
||||
|
||||
/* h = h % (2^128) */
|
||||
h0 = ((h0 ) | (h1 << 26)) & 0xffffffff;
|
||||
h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
|
||||
h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
|
||||
h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
|
||||
|
||||
/* mac = (h + pad) % (2^128) */
|
||||
f = (unsigned long long)h0 + st->pad[0] ; h0 = (unsigned long)f;
|
||||
f = (unsigned long long)h1 + st->pad[1] + (f >> 32); h1 = (unsigned long)f;
|
||||
f = (unsigned long long)h2 + st->pad[2] + (f >> 32); h2 = (unsigned long)f;
|
||||
f = (unsigned long long)h3 + st->pad[3] + (f >> 32); h3 = (unsigned long)f;
|
||||
|
||||
U32TO8(mac + 0, h0);
|
||||
U32TO8(mac + 4, h1);
|
||||
U32TO8(mac + 8, h2);
|
||||
U32TO8(mac + 12, h3);
|
||||
|
||||
/* zero out the state */
|
||||
st->h[0] = 0;
|
||||
st->h[1] = 0;
|
||||
st->h[2] = 0;
|
||||
st->h[3] = 0;
|
||||
st->h[4] = 0;
|
||||
st->r[0] = 0;
|
||||
st->r[1] = 0;
|
||||
st->r[2] = 0;
|
||||
st->r[3] = 0;
|
||||
st->r[4] = 0;
|
||||
st->pad[0] = 0;
|
||||
st->pad[1] = 0;
|
||||
st->pad[2] = 0;
|
||||
st->pad[3] = 0;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -0,0 +1,41 @@
|
||||
// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT
|
||||
|
||||
#include "poly1305-donna.h"
|
||||
#include "poly1305-donna-32.h"
|
||||
|
||||
void
|
||||
poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes) {
|
||||
poly1305_state_internal_t *st = (poly1305_state_internal_t *)ctx;
|
||||
size_t i;
|
||||
|
||||
/* handle leftover */
|
||||
if (st->leftover) {
|
||||
size_t want = (poly1305_block_size - st->leftover);
|
||||
if (want > bytes)
|
||||
want = bytes;
|
||||
for (i = 0; i < want; i++)
|
||||
st->buffer[st->leftover + i] = m[i];
|
||||
bytes -= want;
|
||||
m += want;
|
||||
st->leftover += want;
|
||||
if (st->leftover < poly1305_block_size)
|
||||
return;
|
||||
poly1305_blocks(st, st->buffer, poly1305_block_size);
|
||||
st->leftover = 0;
|
||||
}
|
||||
|
||||
/* process full blocks */
|
||||
if (bytes >= poly1305_block_size) {
|
||||
size_t want = (bytes & ~(poly1305_block_size - 1));
|
||||
poly1305_blocks(st, m, want);
|
||||
m += want;
|
||||
bytes -= want;
|
||||
}
|
||||
|
||||
/* store leftover */
|
||||
if (bytes) {
|
||||
for (i = 0; i < bytes; i++)
|
||||
st->buffer[st->leftover + i] = m[i];
|
||||
st->leftover += bytes;
|
||||
}
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
// Taken from https://github.com/floodyberry/poly1305-donna - public domain or MIT
|
||||
#ifndef POLY1305_DONNA_H
|
||||
#define POLY1305_DONNA_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
typedef struct poly1305_context {
|
||||
size_t aligner;
|
||||
unsigned char opaque[136];
|
||||
} poly1305_context;
|
||||
|
||||
void poly1305_init(poly1305_context *ctx, const unsigned char key[32]);
|
||||
void poly1305_update(poly1305_context *ctx, const unsigned char *m, size_t bytes);
|
||||
void poly1305_finish(poly1305_context *ctx, unsigned char mac[16]);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* POLY1305_DONNA_H */
|
259
lib/lib_ssl/esp_wireguard-Tasmota/src/esp_wireguard.cpp
Normal file
259
lib/lib_ssl/esp_wireguard-Tasmota/src/esp_wireguard.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>
|
||||
* Copyright (c) 2023-2024 Simone Rossetto <simros85@gmail.com>
|
||||
* Copyright (c) 2025 Stephan Hadinger
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "esp_wireguard.h"
|
||||
|
||||
#include "WiFiHelper.h"
|
||||
|
||||
#include "lwip/ip.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/netdb.h"
|
||||
#include "lwip/dns.h"
|
||||
#include "lwip/err.h"
|
||||
|
||||
#include "wireguard-platform.h"
|
||||
#include "wireguardif.h"
|
||||
|
||||
//**************************************************************************************************************
|
||||
// enable AddLog support within a C++ library
|
||||
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
|
||||
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
|
||||
//**************************************************************************************************************
|
||||
|
||||
#if defined(CONFIG_LWIP_IPV6)
|
||||
#define WG_ADDRSTRLEN INET6_ADDRSTRLEN
|
||||
#else
|
||||
#define WG_ADDRSTRLEN INET_ADDRSTRLEN
|
||||
#endif
|
||||
|
||||
static struct netif wg_netif_struct = {0};
|
||||
static struct netif *wg_netif = NULL;
|
||||
static wireguardif_peer_t peer = {0};
|
||||
static uint8_t wireguard_peer_index = WIREGUARDIF_INVALID_INDEX;
|
||||
|
||||
static esp_err_t esp_wireguard_peer_init(const wireguard_config_t *config, wireguardif_peer_t *peer)
|
||||
{
|
||||
if (!config || !peer) { return ESP_ERR_INVALID_ARG; }
|
||||
|
||||
if (ip_addr_isany(&(config->endpoint_ip))) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : peer_init: invalid endpoint ip: `%s`"), ipaddr_ntoa(&(config->endpoint_ip)));
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
ip_addr_copy(peer->endpoint_ip, config->endpoint_ip);
|
||||
|
||||
memmove(peer->public_key2, config->public_key2, WG_KEY_LEN);
|
||||
memmove(peer->preshared_key2, config->preshared_key2, WG_KEY_LEN);
|
||||
peer->keep_alive = config->persistent_keepalive;
|
||||
|
||||
/* Allow device's own address through tunnel */
|
||||
peer->allowed_ip = config->address2;
|
||||
peer->allowed_mask = config->subnet;
|
||||
|
||||
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : Default allowed_ips %s/%s"), IPAddress(&peer->allowed_ip).toString().c_str(),
|
||||
IPAddress(&peer->allowed_mask).toString().c_str());
|
||||
|
||||
peer->endport_port = config->port;
|
||||
peer->keep_alive = config->persistent_keepalive;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t esp_wireguard_netif_create(const wireguard_config_t *config)
|
||||
{
|
||||
esp_err_t err;
|
||||
ip_addr_t gateway = IPADDR4_INIT_BYTES(0, 0, 0, 0);
|
||||
struct wireguardif_init_data wg = {0};
|
||||
|
||||
if (!config) { return ESP_ERR_INVALID_ARG;}
|
||||
|
||||
/* Setup the WireGuard device structure */
|
||||
memmove(wg.private_key2, config->private_key2, WG_KEY_LEN);
|
||||
wg.listen_port = config->listen_port;
|
||||
wg.bind_netif = NULL;
|
||||
|
||||
/* Register the new WireGuard network interface with lwIP */
|
||||
// AddLog(LOG_LEVEL_DEBUG, "WG : Creating netif addr %_I mask %_I gateway %_I",
|
||||
// config->address2.u_addr.ip4.addr, config->netmask2.u_addr.ip4.addr, gateway.u_addr.ip4.addr);
|
||||
|
||||
wg_netif = netif_add(
|
||||
&wg_netif_struct,
|
||||
ip_2_ip4(&config->address2),
|
||||
ip_2_ip4(&config->netmask2),
|
||||
ip_2_ip4(&gateway),
|
||||
&wg, &wireguardif_init,
|
||||
&ip_input);
|
||||
|
||||
if (wg_netif == NULL) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : netif_add: failed"));
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Mark the interface as administratively up, link up flag is set
|
||||
* automatically when peer connects */
|
||||
netif_set_up(wg_netif);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_wireguard_init(wireguard_config_t *config, wireguard_ctx_t *ctx)
|
||||
{
|
||||
if (!config || !ctx) { return ESP_ERR_INVALID_ARG; }
|
||||
ctx->config = config;
|
||||
ctx->netif = NULL;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_wireguard_connect(wireguard_ctx_t *ctx)
|
||||
{
|
||||
esp_err_t err = ESP_FAIL;
|
||||
err_t lwip_err = -1;
|
||||
IPAddress remote_addr;
|
||||
|
||||
if (!ctx) {
|
||||
err = ESP_ERR_INVALID_ARG;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ctx->netif == NULL) {
|
||||
err = esp_wireguard_netif_create(ctx->config);
|
||||
if (err != ESP_OK) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : netif_create err %d"), err);
|
||||
goto fail;
|
||||
}
|
||||
ctx->netif = wg_netif;
|
||||
// ctx->netif_default = netif_default;
|
||||
}
|
||||
|
||||
// Add include "ESP8266WiFi.h" for this to work
|
||||
if (!WiFiHelper::hostByName(ctx->config->endpoint, remote_addr)) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : hostByName failed '%s'"), ctx->config->endpoint);
|
||||
goto fail;
|
||||
}
|
||||
#ifdef ESP32
|
||||
remote_addr.to_ip_addr_t(&ctx->config->endpoint_ip);
|
||||
#else
|
||||
ctx->config->endpoint_ip = remote_addr;
|
||||
#endif
|
||||
AddLog(LOG_LEVEL_DEBUG_MORE, PSTR("WG : hostByName '%s' resolved to %s"), ctx->config->endpoint, remote_addr.toString().c_str());
|
||||
|
||||
/* Initialize the first WireGuard peer structure */
|
||||
err = esp_wireguard_peer_init(ctx->config, &peer);
|
||||
if (err != ESP_OK) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : peer_init: %d"), err);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Register the new WireGuard peer with the network interface */
|
||||
lwip_err = wireguardif_add_peer(ctx->netif, &peer, &wireguard_peer_index);
|
||||
if (lwip_err != ERR_OK || wireguard_peer_index == WIREGUARDIF_INVALID_INDEX) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : wireguardif_add_peer: %i"), lwip_err);
|
||||
err = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
if (ip_addr_isany(&peer.endpoint_ip)) {
|
||||
err = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : Connecting to %s (%s) port %i"), ctx->config->endpoint, ipaddr_ntoa(&(peer.endpoint_ip)), peer.endport_port);
|
||||
lwip_err = wireguardif_connect(ctx->netif, wireguard_peer_index);
|
||||
if (lwip_err != ERR_OK) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : wireguardif_connect: %i"), lwip_err);
|
||||
err = ESP_FAIL;
|
||||
goto fail;
|
||||
}
|
||||
err = ESP_OK;
|
||||
fail:
|
||||
// AddLog(LOG_LEVEL_DEBUG, ">>>: OUT esp_wireguard_connect %d", err);
|
||||
return err;
|
||||
}
|
||||
|
||||
esp_err_t esp_wireguard_disconnect(wireguard_ctx_t *ctx)
|
||||
{
|
||||
err_t lwip_err;
|
||||
if (!ctx || !ctx->netif) { return ESP_ERR_INVALID_ARG; }
|
||||
|
||||
// Clear the IP address to gracefully disconnect any clients while the
|
||||
// peers are still valid
|
||||
netif_set_ipaddr(ctx->netif, IP4_ADDR_ANY4);
|
||||
|
||||
lwip_err = wireguardif_disconnect(ctx->netif, wireguard_peer_index);
|
||||
if (lwip_err != ERR_OK) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : wireguardif_disconnect: peer_index: %" PRIu8 " err: %i"), wireguard_peer_index, lwip_err);
|
||||
}
|
||||
|
||||
lwip_err = wireguardif_remove_peer(ctx->netif, wireguard_peer_index);
|
||||
if (lwip_err != ERR_OK) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : wireguardif_remove_peer: peer_index: %" PRIu8 " err: %i"), wireguard_peer_index, lwip_err);
|
||||
}
|
||||
|
||||
wireguard_peer_index = WIREGUARDIF_INVALID_INDEX;
|
||||
wireguardif_shutdown(ctx->netif);
|
||||
netif_remove(ctx->netif);
|
||||
wireguardif_fini(ctx->netif);
|
||||
ctx->netif = NULL;
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_wireguard_peer_is_up(const wireguard_ctx_t *ctx)
|
||||
{
|
||||
err_t lwip_err;
|
||||
if (!ctx || !ctx->netif) { return ESP_ERR_INVALID_ARG; }
|
||||
|
||||
lwip_err = wireguardif_peer_is_up(
|
||||
ctx->netif,
|
||||
wireguard_peer_index,
|
||||
&peer.endpoint_ip,
|
||||
&peer.endport_port);
|
||||
|
||||
return (lwip_err != ERR_OK) ? ESP_FAIL : ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_wireguard_latest_handshake(const wireguard_ctx_t *ctx, time_t *result)
|
||||
{
|
||||
if (!ctx || !ctx->netif) { return ESP_ERR_INVALID_ARG; }
|
||||
|
||||
*result = wireguardif_latest_handshake(ctx->netif, wireguard_peer_index);
|
||||
return (*result > 0) ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
|
||||
esp_err_t esp_wireguard_add_allowed_ip(const wireguard_ctx_t *ctx, const ip_addr_t& allowed_ip, const ip_addr_t& allowed_ip_mask)
|
||||
{
|
||||
err_t lwip_err;
|
||||
if (!ctx || !ctx->netif) { return ESP_ERR_INVALID_ARG; }
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, "WG : Added allowed_ips %_I/%_I", allowed_ip.u_addr.ip4.addr, allowed_ip_mask.u_addr.ip4.addr);
|
||||
lwip_err = wireguardif_add_allowed_ip(ctx->netif, wireguard_peer_index, allowed_ip, allowed_ip_mask);
|
||||
return (lwip_err == ERR_OK ? ESP_OK : ESP_FAIL);
|
||||
}
|
63
lib/lib_ssl/esp_wireguard-Tasmota/src/esp_wireguard_err.h
Normal file
63
lib/lib_ssl/esp_wireguard-Tasmota/src/esp_wireguard_err.h
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2024 Simone Rossetto <simros85@gmail.com>
|
||||
*
|
||||
* 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 above 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. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
|
||||
*/
|
||||
|
||||
#if !defined(__ESP_WIREGUARD_ERR__H__)
|
||||
#define __ESP_WIREGUARD_ERR__H__
|
||||
|
||||
#if defined(ESP8266)
|
||||
typedef int esp_err_t;
|
||||
|
||||
#define ESP_OK 0 /*!< esp_err_t value indicating success (no error) */
|
||||
#define ESP_FAIL -1 /*!< Generic esp_err_t code indicating failure */
|
||||
|
||||
#define ESP_ERR_NO_MEM 0x101 /*!< Out of memory */
|
||||
#define ESP_ERR_INVALID_ARG 0x102 /*!< Invalid argument */
|
||||
#define ESP_ERR_INVALID_STATE 0x103 /*!< Invalid state */
|
||||
#define ESP_ERR_INVALID_SIZE 0x104 /*!< Invalid size */
|
||||
#define ESP_ERR_NOT_FOUND 0x105 /*!< Requested resource not found */
|
||||
#define ESP_ERR_NOT_SUPPORTED 0x106 /*!< Operation or feature not supported */
|
||||
#define ESP_ERR_TIMEOUT 0x107 /*!< Operation timed out */
|
||||
#define ESP_ERR_INVALID_RESPONSE 0x108 /*!< Received response was invalid */
|
||||
#define ESP_ERR_INVALID_CRC 0x109 /*!< CRC or checksum was invalid */
|
||||
#define ESP_ERR_INVALID_VERSION 0x10A /*!< Version was invalid */
|
||||
#define ESP_ERR_INVALID_MAC 0x10B /*!< MAC address was invalid */
|
||||
#define ESP_ERR_NOT_FINISHED 0x10C /*!< There are items remained to retrieve */
|
||||
|
||||
#define ESP_ERR_WIFI_BASE 0x3000 /*!< Starting number of WiFi error codes */
|
||||
#define ESP_ERR_MESH_BASE 0x4000 /*!< Starting number of MESH error codes */
|
||||
#define ESP_ERR_FLASH_BASE 0x6000 /*!< Starting number of flash error codes */
|
||||
#define ESP_ERR_HW_CRYPTO_BASE 0xc000 /*!< Starting number of HW cryptography module error codes */
|
||||
#define ESP_ERR_MEMPROT_BASE 0xd000 /*!< Starting number of Memory Protection API error codes */
|
||||
|
||||
#else
|
||||
#include <esp_err.h>
|
||||
#endif
|
||||
|
||||
#endif // __ESP_WIREGUARD_ERR__H__
|
83
lib/lib_ssl/esp_wireguard-Tasmota/src/tasmota_crypto.cpp
Normal file
83
lib/lib_ssl/esp_wireguard-Tasmota/src/tasmota_crypto.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
/*
|
||||
tasmota_crypto.cpp - crypto layer to call bearssl from wireguard
|
||||
|
||||
Copyright (C) 2025 Theo Arends, Stephan Hadinger
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <t_bearssl.h>
|
||||
#ifdef ESP32
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "esp_expression_with_stack.h"
|
||||
#endif // ESP32
|
||||
|
||||
//**************************************************************************************************************
|
||||
// enable AddLog support within a C++ library
|
||||
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
|
||||
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
|
||||
//**************************************************************************************************************
|
||||
|
||||
#define BR_EC25519_IMPL br_ec_c25519_m15 // BearSSL implementation for Curve 25519
|
||||
|
||||
// we need a global
|
||||
static struct dh_curve25519_t {
|
||||
uint8_t q[32];
|
||||
uint8_t n[32];
|
||||
int result;
|
||||
} g_dh_curve25519;
|
||||
|
||||
static void run_crypto_scalarmult_curve25519(void) {
|
||||
g_dh_curve25519.result = BR_EC25519_IMPL.mul(g_dh_curve25519.q, 32, g_dh_curve25519.n, 32, BR_EC_curve25519);
|
||||
}
|
||||
|
||||
extern "C" int crypto_scalarmult_curve25519(unsigned char *q, const unsigned char *n,const unsigned char *p) {
|
||||
for (int32_t i=0; i<32; i++) {
|
||||
g_dh_curve25519.q[i] = p[i];
|
||||
g_dh_curve25519.n[i] = n[31-i];
|
||||
}
|
||||
|
||||
#ifdef ESP32
|
||||
const char *taskName = pcTaskGetName(NULL);
|
||||
if (uxTaskGetStackHighWaterMark(nullptr) < 2000) {
|
||||
//Allocate a stack buffer, from heap or as a static form:
|
||||
StackType_t *shared_stack = (StackType_t*) heap_caps_malloc(4096 * sizeof(StackType_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL);
|
||||
//Allocate a mutex to protect its usage:
|
||||
SemaphoreHandle_t lock = xSemaphoreCreateMutex();
|
||||
//Call the desired function using the macro helper:
|
||||
esp_execute_shared_stack_function(lock,
|
||||
shared_stack,
|
||||
4096,
|
||||
run_crypto_scalarmult_curve25519);
|
||||
|
||||
vSemaphoreDelete(lock);
|
||||
free(shared_stack);
|
||||
} else {
|
||||
run_crypto_scalarmult_curve25519();
|
||||
}
|
||||
#else
|
||||
run_crypto_scalarmult_curve25519();
|
||||
#endif
|
||||
|
||||
if (g_dh_curve25519.result) {
|
||||
for (int32_t i=0; i<32; i++) {
|
||||
q[i] = g_dh_curve25519.q[i];
|
||||
}
|
||||
return 0; // Success
|
||||
} else {
|
||||
return 1; // Failure
|
||||
}
|
||||
}
|
76
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguard-platform.cpp
Normal file
76
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguard-platform.cpp
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) 2022 Tomoyuki Sakurai <y@trombik.org>
|
||||
* Copyright (c) 2023-2024 Simone Rossetto <simros85@gmail.com>
|
||||
* Copyright (c) 2025 Stephan Hadinger
|
||||
*
|
||||
* 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 above 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. Neither the name of the copyright holder nor the names of its contributors
|
||||
* may be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT HOLDER 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.
|
||||
*/
|
||||
|
||||
#include "wireguard-platform.h"
|
||||
|
||||
#include "lwip/sys.h"
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
#define ENTROPY_MINIMUM_REQUIRED_THRESHOLD (134)
|
||||
#define ENTROPY_FUNCTION_DATA NULL
|
||||
#define ENTROPY_CUSTOM_DATA NULL
|
||||
#define ENTROPY_CUSTOM_DATA_LENGTH (0)
|
||||
|
||||
extern "C" uint32_t phy_get_rand(void); // From the ESP8266 SDK
|
||||
void wireguard_random_bytes(void *bytes, size_t size) {
|
||||
#ifdef ESP32
|
||||
esp_fill_random(bytes, size);
|
||||
#elif defined(ESP8266)
|
||||
for (int32_t i=0; i<size; i++) {
|
||||
*((uint8_t*)bytes + i) = phy_get_rand();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
uint32_t wireguard_sys_now() {
|
||||
// Default to the LwIP system time
|
||||
return sys_now();
|
||||
}
|
||||
|
||||
void wireguard_tai64n_now(uint8_t *output) {
|
||||
// See https://cr.yp.to/libtai/tai64.html
|
||||
// 64 bit seconds from 1970 = 8 bytes
|
||||
// 32 bit nano seconds from current second
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
|
||||
uint64_t seconds = 0x400000000000000aULL + tv.tv_sec;
|
||||
uint32_t nanos = tv.tv_usec * 1000;
|
||||
U64TO8_BIG(output + 0, seconds);
|
||||
U32TO8_BIG(output + 8, nanos);
|
||||
}
|
||||
|
||||
bool wireguard_is_under_load() { // actually not called, we have inlined the fact that it's always false
|
||||
return false;
|
||||
}
|
71
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguard-platform.h
Normal file
71
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguard-platform.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
||||
* Copyright (c) 2024 Simone Rossetto <simros85@gmail.com>
|
||||
* Copyright (c) 2025 Stephan Hadinger
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
* Author: Daniel Hope <daniel.hope@smartalock.com>
|
||||
*/
|
||||
|
||||
#ifndef _WIREGUARD_PLATFORM_H_
|
||||
#define _WIREGUARD_PLATFORM_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define WIREGUARD_MAX_PEERS (1)
|
||||
#define WIREGUARD_MAX_SRC_IPS (4) // TASMOTA
|
||||
|
||||
// Per device limit on accepting (valid) initiation requests - per peer
|
||||
#define MAX_INITIATIONS_PER_SECOND (2)
|
||||
|
||||
// The number of milliseconds since system boot - for LwIP systems this could be sys_now()
|
||||
uint32_t wireguard_sys_now();
|
||||
|
||||
// Fill the supplied buffer with random data - random data is used for generating new session keys periodically
|
||||
void wireguard_random_bytes(void *bytes, size_t size);
|
||||
|
||||
// Get the current time in tai64n format - 8 byte seconds, 4 byte nano sub-second - see https://cr.yp.to/libtai/tai64.html for details
|
||||
// Output buffer passed is 12 bytes
|
||||
// The Wireguard implementation doesn't strictly need this to be a time, but instead an increasing value
|
||||
// The remote end of the Wireguard tunnel will use this value in handshake replay detection
|
||||
void wireguard_tai64n_now(uint8_t *output);
|
||||
|
||||
// Is the system under load - i.e. should we generate cookie reply message in response to initiation messages
|
||||
bool wireguard_is_under_load();
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _WIREGUARD_PLATFORM_H_ */
|
997
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguard.cpp
Normal file
997
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguard.cpp
Normal file
@ -0,0 +1,997 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
||||
* Copyright (c) 2024 Simone Rossetto <simros85@gmail.com>
|
||||
* Copyright (c) 2025 Stephan Hadinger
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
* Author: Daniel Hope <daniel.hope@smartalock.com>
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "wireguard.h"
|
||||
|
||||
#include "crypto.h"
|
||||
|
||||
// For HMAC calculation
|
||||
#define WIREGUARD_BLAKE2S_BLOCK_SIZE (64)
|
||||
|
||||
//**************************************************************************************************************
|
||||
// enable AddLog support within a C++ library
|
||||
extern void AddLog(uint32_t loglevel, PGM_P formatP, ...);
|
||||
enum LoggingLevels {LOG_LEVEL_NONE, LOG_LEVEL_ERROR, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG, LOG_LEVEL_DEBUG_MORE};
|
||||
//**************************************************************************************************************
|
||||
|
||||
// 5.4 Messages
|
||||
// Constants
|
||||
static const uint8_t PROGMEM CONSTRUCTION[] = "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s"; // The UTF-8 string literal "Noise_IKpsk2_25519_ChaChaPoly_BLAKE2s", 37 bytes of output
|
||||
static const uint8_t PROGMEM IDENTIFIER[] = "WireGuard v1 zx2c4 Jason@zx2c4.com"; // The UTF-8 string literal "WireGuard v1 zx2c4 Jason@zx2c4.com", 34 bytes of output
|
||||
static const uint8_t PROGMEM LABEL_MAC1[] = "mac1----"; // Label-Mac1 The UTF-8 string literal "mac1----", 8 bytes of output.
|
||||
static const uint8_t PROGMEM LABEL_COOKIE[] = "cookie--"; // Label-Cookie The UTF-8 string literal "cookie--", 8 bytes of output
|
||||
|
||||
static const uint8_t zero_key[WIREGUARD_PUBLIC_KEY_LEN] = { 0 };
|
||||
|
||||
// Calculated in wireguard_init
|
||||
static uint8_t construction_hash[WIREGUARD_HASH_LEN];
|
||||
static uint8_t identifier_hash[WIREGUARD_HASH_LEN];
|
||||
|
||||
|
||||
void wireguard_init() {
|
||||
wireguard_blake2s_ctx ctx;
|
||||
// Pre-calculate chaining key hash
|
||||
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);
|
||||
wireguard_blake2s_update(&ctx, CONSTRUCTION, sizeof(CONSTRUCTION) - 1); // remove terminating NULL char
|
||||
wireguard_blake2s_final(&ctx, construction_hash);
|
||||
// Pre-calculate initial handshake hash - uses construction_hash calculated above
|
||||
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);
|
||||
wireguard_blake2s_update(&ctx, construction_hash, sizeof(construction_hash));
|
||||
wireguard_blake2s_update(&ctx, IDENTIFIER, sizeof(IDENTIFIER) - 1); // remove terminating NULL char
|
||||
wireguard_blake2s_final(&ctx, identifier_hash);
|
||||
}
|
||||
|
||||
wireguard_peer_t *wireguard_peer_alloc(wireguard_device_t *device) {
|
||||
for (uint32_t x=0; x < WIREGUARD_MAX_PEERS; x++) {
|
||||
if (!device->peers[x].valid) {
|
||||
return &device->peers[x];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wireguard_peer_t *wireguard_peer_lookup_by_pubkey(struct wireguard_device *device, uint8_t *public_key) {
|
||||
wireguard_peer_t *result = NULL;
|
||||
wireguard_peer_t *tmp;
|
||||
for (int32_t x=0; x < WIREGUARD_MAX_PEERS; x++) {
|
||||
if (device->peers[x].valid) {
|
||||
if (memcmp(device->peers[x].public_key, public_key, WIREGUARD_PUBLIC_KEY_LEN) == 0) {
|
||||
return &device->peers[x];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint8_t wireguard_peer_index(struct wireguard_device *device, wireguard_peer_t *peer) {
|
||||
for (int32_t x=0; x < WIREGUARD_MAX_PEERS; x++) {
|
||||
if (peer == &device->peers[x]) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
return 0xFF; // not found
|
||||
}
|
||||
|
||||
wireguard_peer_t *wireguard_peer_lookup_by_peer_index(struct wireguard_device *device, uint8_t peer_index) {
|
||||
if (peer_index < WIREGUARD_MAX_PEERS) {
|
||||
if (device->peers[peer_index].valid) {
|
||||
return &device->peers[peer_index];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wireguard_peer_t *wireguard_peer_lookup_by_receiver(struct wireguard_device *device, uint32_t receiver) {
|
||||
for (int32_t x=0; x < WIREGUARD_MAX_PEERS; x++) {
|
||||
wireguard_peer_t *cur_key = &device->peers[x];
|
||||
if (cur_key->valid) {
|
||||
if ((cur_key->curr_keypair.valid && (cur_key->curr_keypair.local_index == receiver)) ||
|
||||
(cur_key->next_keypair.valid && (cur_key->next_keypair.local_index == receiver)) ||
|
||||
(cur_key->prev_keypair.valid && (cur_key->prev_keypair.local_index == receiver))
|
||||
) {
|
||||
return cur_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
wireguard_peer_t *wireguard_peer_lookup_by_handshake(struct wireguard_device *device, uint32_t receiver) {
|
||||
for (int32_t x=0; x < WIREGUARD_MAX_PEERS; x++) {
|
||||
wireguard_peer_t *cur_key = &device->peers[x];
|
||||
if (cur_key->valid) {
|
||||
if (cur_key->handshake.valid && cur_key->handshake.initiator && (cur_key->handshake.local_index == receiver)) {
|
||||
return cur_key;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool wireguard_expired(uint32_t created_millis, uint32_t valid_seconds) {
|
||||
uint32_t diff = wireguard_sys_now() - created_millis;
|
||||
return (diff >= (valid_seconds * 1000));
|
||||
}
|
||||
|
||||
|
||||
static void generate_cookie_secret(struct wireguard_device *device) {
|
||||
wireguard_random_bytes(device->cookie_secret, WIREGUARD_HASH_LEN);
|
||||
device->cookie_secret_millis = wireguard_sys_now();
|
||||
}
|
||||
|
||||
static void generate_peer_cookie(struct wireguard_device *device, uint8_t *cookie, uint8_t *source_addr_port, size_t source_length) {
|
||||
wireguard_blake2s_ctx ctx;
|
||||
|
||||
if (wireguard_expired(device->cookie_secret_millis, COOKIE_SECRET_MAX_AGE)) {
|
||||
// Generate new random bytes
|
||||
generate_cookie_secret(device);
|
||||
}
|
||||
|
||||
// Mac(key, input) Keyed-Blake2s(key, input, 16), the keyed MAC variant of the BLAKE2s hash function, returning 16 bytes of output
|
||||
wireguard_blake2s_init(&ctx, WIREGUARD_COOKIE_LEN, device->cookie_secret, WIREGUARD_HASH_LEN);
|
||||
// 5.4.7 Under Load: Cookie Reply Message
|
||||
// Mix in the IP address and port - have the IP layer pass this in as byte array to avoid using Lwip specific APIs in this module
|
||||
if ((source_addr_port) && (source_length > 0)) {
|
||||
wireguard_blake2s_update(&ctx, source_addr_port, source_length);
|
||||
}
|
||||
wireguard_blake2s_final(&ctx, cookie);
|
||||
}
|
||||
|
||||
static void wireguard_mac(uint8_t *dst, const void *message, size_t len, const uint8_t *key, size_t keylen) {
|
||||
wireguard_blake2s(dst, WIREGUARD_COOKIE_LEN, key, keylen, message, len);
|
||||
}
|
||||
|
||||
static void wireguard_mac_key(uint8_t *key, const uint8_t *public_key, const uint8_t *label, size_t label_len) {
|
||||
blake2s_ctx ctx;
|
||||
blake2s_init(&ctx, WIREGUARD_SESSION_KEY_LEN, NULL, 0);
|
||||
blake2s_update(&ctx, label, label_len);
|
||||
blake2s_update(&ctx, public_key, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
blake2s_final(&ctx, key);
|
||||
}
|
||||
|
||||
static void wireguard_mix_hash(uint8_t *hash, const uint8_t *src, size_t src_len) {
|
||||
wireguard_blake2s_ctx ctx;
|
||||
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0);
|
||||
wireguard_blake2s_update(&ctx, hash, WIREGUARD_HASH_LEN);
|
||||
wireguard_blake2s_update(&ctx, src, src_len);
|
||||
wireguard_blake2s_final(&ctx, hash);
|
||||
}
|
||||
|
||||
static void wireguard_hmac(uint8_t *digest, const uint8_t *key, size_t key_len, const uint8_t *text, size_t text_len) {
|
||||
// Adapted from appendix example in RFC2104 to use BLAKE2S instead of MD5 - https://tools.ietf.org/html/rfc2104
|
||||
wireguard_blake2s_ctx ctx;
|
||||
uint8_t k_ipad[WIREGUARD_BLAKE2S_BLOCK_SIZE]; // inner padding - key XORd with ipad
|
||||
uint8_t k_opad[WIREGUARD_BLAKE2S_BLOCK_SIZE]; // outer padding - key XORd with opad
|
||||
|
||||
uint8_t tk[WIREGUARD_HASH_LEN];
|
||||
int i;
|
||||
// if key is longer than BLAKE2S_BLOCK_SIZE bytes reset it to key=BLAKE2S(key)
|
||||
if (key_len > WIREGUARD_BLAKE2S_BLOCK_SIZE) {
|
||||
wireguard_blake2s_ctx tctx;
|
||||
wireguard_blake2s_init(&tctx, WIREGUARD_HASH_LEN, NULL, 0);
|
||||
wireguard_blake2s_update(&tctx, key, key_len);
|
||||
wireguard_blake2s_final(&tctx, tk);
|
||||
key = tk;
|
||||
key_len = WIREGUARD_HASH_LEN;
|
||||
}
|
||||
|
||||
// the HMAC transform looks like:
|
||||
// HASH(K XOR opad, HASH(K XOR ipad, text))
|
||||
// where K is an n byte key
|
||||
// ipad is the byte 0x36 repeated BLAKE2S_BLOCK_SIZE times
|
||||
// opad is the byte 0x5c repeated BLAKE2S_BLOCK_SIZE times
|
||||
// and text is the data being protected
|
||||
memset(k_ipad, 0, sizeof(k_ipad));
|
||||
memset(k_opad, 0, sizeof(k_opad));
|
||||
memcpy(k_ipad, key, key_len);
|
||||
memcpy(k_opad, key, key_len);
|
||||
|
||||
// XOR key with ipad and opad values
|
||||
for (i=0; i < WIREGUARD_BLAKE2S_BLOCK_SIZE; i++) {
|
||||
k_ipad[i] ^= 0x36;
|
||||
k_opad[i] ^= 0x5c;
|
||||
}
|
||||
// perform inner HASH
|
||||
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0); // init context for 1st pass
|
||||
wireguard_blake2s_update(&ctx, k_ipad, WIREGUARD_BLAKE2S_BLOCK_SIZE); // start with inner pad
|
||||
wireguard_blake2s_update(&ctx, text, text_len); // then text of datagram
|
||||
wireguard_blake2s_final(&ctx, digest); // finish up 1st pass
|
||||
|
||||
// perform outer HASH
|
||||
wireguard_blake2s_init(&ctx, WIREGUARD_HASH_LEN, NULL, 0); // init context for 2nd pass
|
||||
wireguard_blake2s_update(&ctx, k_opad, WIREGUARD_BLAKE2S_BLOCK_SIZE); // start with outer pad
|
||||
wireguard_blake2s_update(&ctx, digest, WIREGUARD_HASH_LEN); // then results of 1st hash
|
||||
wireguard_blake2s_final(&ctx, digest); // finish up 2nd pass
|
||||
}
|
||||
|
||||
static void wireguard_kdf1(uint8_t *tau1, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {
|
||||
uint8_t tau0[WIREGUARD_HASH_LEN];
|
||||
uint8_t output[WIREGUARD_HASH_LEN + 1];
|
||||
|
||||
// tau0 = Hmac(key, input)
|
||||
wireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);
|
||||
// tau1 := Hmac(tau0, 0x1)
|
||||
output[0] = 1;
|
||||
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);
|
||||
memcpy(tau1, output, WIREGUARD_HASH_LEN);
|
||||
|
||||
// Wipe intermediates
|
||||
crypto_zero(tau0, sizeof(tau0));
|
||||
crypto_zero(output, sizeof(output));
|
||||
}
|
||||
|
||||
static void wireguard_kdf2(uint8_t *tau1, uint8_t *tau2, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {
|
||||
uint8_t tau0[WIREGUARD_HASH_LEN];
|
||||
uint8_t output[WIREGUARD_HASH_LEN + 1];
|
||||
|
||||
// tau0 = Hmac(key, input)
|
||||
wireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);
|
||||
// tau1 := Hmac(tau0, 0x1)
|
||||
output[0] = 1;
|
||||
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);
|
||||
memcpy(tau1, output, WIREGUARD_HASH_LEN);
|
||||
|
||||
// tau2 := Hmac(tau0,tau1 || 0x2)
|
||||
output[WIREGUARD_HASH_LEN] = 2;
|
||||
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);
|
||||
memcpy(tau2, output, WIREGUARD_HASH_LEN);
|
||||
|
||||
// Wipe intermediates
|
||||
crypto_zero(tau0, sizeof(tau0));
|
||||
crypto_zero(output, sizeof(output));
|
||||
}
|
||||
|
||||
static void wireguard_kdf3(uint8_t *tau1, uint8_t *tau2, uint8_t *tau3, const uint8_t *chaining_key, const uint8_t *data, size_t data_len) {
|
||||
uint8_t tau0[WIREGUARD_HASH_LEN];
|
||||
uint8_t output[WIREGUARD_HASH_LEN + 1];
|
||||
|
||||
// tau0 = Hmac(key, input)
|
||||
wireguard_hmac(tau0, chaining_key, WIREGUARD_HASH_LEN, data, data_len);
|
||||
// tau1 := Hmac(tau0, 0x1)
|
||||
output[0] = 1;
|
||||
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, 1);
|
||||
memcpy(tau1, output, WIREGUARD_HASH_LEN);
|
||||
|
||||
// tau2 := Hmac(tau0,tau1 || 0x2)
|
||||
output[WIREGUARD_HASH_LEN] = 2;
|
||||
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);
|
||||
memcpy(tau2, output, WIREGUARD_HASH_LEN);
|
||||
|
||||
// tau3 := Hmac(tau0,tau1,tau2 || 0x3)
|
||||
output[WIREGUARD_HASH_LEN] = 3;
|
||||
wireguard_hmac(output, tau0, WIREGUARD_HASH_LEN, output, WIREGUARD_HASH_LEN + 1);
|
||||
memcpy(tau3, output, WIREGUARD_HASH_LEN);
|
||||
|
||||
// Wipe intermediates
|
||||
crypto_zero(tau0, sizeof(tau0));
|
||||
crypto_zero(output, sizeof(output));
|
||||
}
|
||||
|
||||
bool wireguard_check_replay(wireguard_keypair_t *keypair, uint64_t seq) {
|
||||
// AddLog(LOG_LEVEL_INFO, ">>>: wireguard_check_replay");
|
||||
// Implementation of packet replay window - as per RFC2401
|
||||
// Adapted from code in Appendix C at https://tools.ietf.org/html/rfc2401
|
||||
uint32_t diff;
|
||||
bool result = false;
|
||||
size_t ReplayWindowSize = sizeof(keypair->replay_bitmap) * CHAR_BIT; // 32 bits
|
||||
|
||||
// WireGuard data packet counter starts from 0 but algorithm expects packet numbers to start from 1
|
||||
seq++;
|
||||
|
||||
if (seq != 0) {
|
||||
if (seq > keypair->replay_counter) {
|
||||
// new larger sequence number
|
||||
diff = seq - keypair->replay_counter;
|
||||
if (diff < ReplayWindowSize) {
|
||||
// In window
|
||||
keypair->replay_bitmap <<= diff;
|
||||
// set bit for this packet
|
||||
keypair->replay_bitmap |= 1;
|
||||
} else {
|
||||
// This packet has a "way larger"
|
||||
keypair->replay_bitmap = 1;
|
||||
}
|
||||
keypair->replay_counter = seq;
|
||||
// larger is good
|
||||
result = true;
|
||||
} else {
|
||||
diff = keypair->replay_counter - seq;
|
||||
if (diff < ReplayWindowSize) {
|
||||
if (keypair->replay_bitmap & ((uint32_t)1 << diff)) {
|
||||
// already seen
|
||||
} else {
|
||||
// mark as seen
|
||||
keypair->replay_bitmap |= ((uint32_t)1 << diff);
|
||||
// out of order but good
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
// too old or wrapped
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// first == 0 or wrapped
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
wireguard_keypair_t *get_peer_keypair_for_idx(wireguard_peer_t *peer, uint32_t idx) {
|
||||
if (peer->curr_keypair.valid && peer->curr_keypair.local_index == idx) {
|
||||
return &peer->curr_keypair;
|
||||
} else if (peer->next_keypair.valid && peer->next_keypair.local_index == idx) {
|
||||
return &peer->next_keypair;
|
||||
} else if (peer->prev_keypair.valid && peer->prev_keypair.local_index == idx) {
|
||||
return &peer->prev_keypair;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static uint32_t wireguard_generate_unique_index(struct wireguard_device *device) {
|
||||
// We need a random 32-bit number but make sure it's not already been used in the context of this device
|
||||
uint32_t result;
|
||||
uint8_t buf[4];
|
||||
wireguard_peer_t *peer;
|
||||
bool existing = false;
|
||||
do {
|
||||
do {
|
||||
wireguard_random_bytes(buf, 4);
|
||||
result = U8TO32_LITTLE(buf);
|
||||
} while ((result == 0) || (result == 0xFFFFFFFF)); // Don't allow 0 or 0xFFFFFFFF as valid values
|
||||
|
||||
existing = false;
|
||||
for (int32_t x=0; x < WIREGUARD_MAX_PEERS; x++) {
|
||||
peer = &device->peers[x];
|
||||
existing = (result == peer->curr_keypair.local_index) ||
|
||||
(result == peer->prev_keypair.local_index) ||
|
||||
(result == peer->next_keypair.local_index) ||
|
||||
(result == peer->handshake.local_index);
|
||||
|
||||
}
|
||||
} while (existing);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void wireguard_clamp_private_key(uint8_t *key) {
|
||||
key[0] &= 248;
|
||||
key[31] = (key[31] & 127) | 64;
|
||||
}
|
||||
|
||||
static void wireguard_generate_private_key(uint8_t *key) {
|
||||
wireguard_random_bytes(key, WIREGUARD_PRIVATE_KEY_LEN);
|
||||
wireguard_clamp_private_key(key);
|
||||
}
|
||||
|
||||
static bool wireguard_generate_public_key(uint8_t *public_key, const uint8_t *private_key) {
|
||||
static const uint8_t basepoint[WIREGUARD_PUBLIC_KEY_LEN] = { 9 };
|
||||
bool result = false;
|
||||
if (memcmp(private_key, zero_key, WIREGUARD_PUBLIC_KEY_LEN) != 0) {
|
||||
result = (wireguard_x25519(public_key, private_key, basepoint) == 0);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool wireguard_check_mac1(struct wireguard_device *device, const uint8_t *data, size_t len, const uint8_t *mac1) {
|
||||
bool result = false;
|
||||
uint8_t calculated[WIREGUARD_COOKIE_LEN];
|
||||
wireguard_mac(calculated, data, len, device->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);
|
||||
if (crypto_equal(calculated, mac1, WIREGUARD_COOKIE_LEN)) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool wireguard_check_mac2(struct wireguard_device *device, const uint8_t *data, size_t len, uint8_t *source_addr_port, size_t source_length, const uint8_t *mac2) {
|
||||
// AddLog(LOG_LEVEL_INFO, ">>>: wireguard_check_mac2");
|
||||
bool result = false;
|
||||
uint8_t cookie[WIREGUARD_COOKIE_LEN];
|
||||
uint8_t calculated[WIREGUARD_COOKIE_LEN];
|
||||
|
||||
generate_peer_cookie(device, cookie, source_addr_port, source_length);
|
||||
|
||||
wireguard_mac(calculated, data, len, cookie, WIREGUARD_COOKIE_LEN);
|
||||
if (crypto_equal(calculated, mac2, WIREGUARD_COOKIE_LEN)) {
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void keypair_destroy(wireguard_keypair_t *keypair) {
|
||||
crypto_zero(keypair, sizeof(wireguard_keypair_t));
|
||||
keypair->valid = false;
|
||||
}
|
||||
|
||||
void keypair_update(wireguard_peer_t *peer, wireguard_keypair_t *received_keypair) {
|
||||
bool key_is_next = (received_keypair == &peer->next_keypair);
|
||||
if (key_is_next) {
|
||||
peer->prev_keypair = peer->curr_keypair;
|
||||
peer->curr_keypair = peer->next_keypair;
|
||||
keypair_destroy(&peer->next_keypair);
|
||||
}
|
||||
}
|
||||
|
||||
static void add_new_keypair(wireguard_peer_t *peer, wireguard_keypair_t new_keypair) {
|
||||
if (new_keypair.initiator) {
|
||||
if (peer->next_keypair.valid) {
|
||||
peer->prev_keypair = peer->next_keypair;
|
||||
keypair_destroy(&peer->next_keypair);
|
||||
} else {
|
||||
peer->prev_keypair = peer->curr_keypair;
|
||||
}
|
||||
peer->curr_keypair = new_keypair;
|
||||
} else {
|
||||
peer->next_keypair = new_keypair;
|
||||
keypair_destroy(&peer->prev_keypair);
|
||||
}
|
||||
peer->latest_handshake_millis = new_keypair.keypair_millis;
|
||||
}
|
||||
|
||||
void wireguard_start_session(wireguard_peer_t *peer, bool initiator) {
|
||||
wireguard_handshake_t *handshake = &peer->handshake;
|
||||
wireguard_keypair_t new_keypair;
|
||||
|
||||
crypto_zero(&new_keypair, sizeof(wireguard_keypair_t));
|
||||
new_keypair.initiator = initiator;
|
||||
new_keypair.local_index = handshake->local_index;
|
||||
new_keypair.remote_index = handshake->remote_index;
|
||||
|
||||
new_keypair.keypair_millis = wireguard_sys_now();
|
||||
new_keypair.sending_valid = true;
|
||||
new_keypair.receiving_valid = true;
|
||||
|
||||
// 5.4.5 Transport Data Key Derivation
|
||||
// (Tsendi = Trecvr, Trecvi = Tsendr) := Kdf2(Ci = Cr,E)
|
||||
if (new_keypair.initiator) {
|
||||
wireguard_kdf2(new_keypair.sending_key, new_keypair.receiving_key, handshake->chaining_key, NULL, 0);
|
||||
} else {
|
||||
wireguard_kdf2(new_keypair.receiving_key, new_keypair.sending_key, handshake->chaining_key, NULL, 0);
|
||||
}
|
||||
|
||||
new_keypair.replay_bitmap = 0;
|
||||
new_keypair.replay_counter = 0;
|
||||
|
||||
new_keypair.last_tx = 0;
|
||||
new_keypair.last_rx = 0; // No packets received yet
|
||||
|
||||
new_keypair.valid = true;
|
||||
|
||||
// Eprivi = Epubi = Eprivr = Epubr = Ci = Cr := E
|
||||
crypto_zero(handshake->ephemeral_private, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
crypto_zero(handshake->remote_ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
crypto_zero(handshake->hash, WIREGUARD_HASH_LEN);
|
||||
crypto_zero(handshake->chaining_key, WIREGUARD_HASH_LEN);
|
||||
handshake->remote_index = 0;
|
||||
handshake->local_index = 0;
|
||||
handshake->valid = false;
|
||||
|
||||
add_new_keypair(peer, new_keypair);
|
||||
}
|
||||
|
||||
uint8_t wireguard_get_message_type(const uint8_t *data, size_t len) {
|
||||
uint8_t result = MESSAGE_INVALID;
|
||||
if (len >= 4) {
|
||||
if ((data[1] == 0) && (data[2] == 0) && (data[3] == 0)) {
|
||||
switch (data[0]) {
|
||||
case MESSAGE_HANDSHAKE_INITIATION:
|
||||
if (len == sizeof(struct message_handshake_initiation)) {
|
||||
result = MESSAGE_HANDSHAKE_INITIATION;
|
||||
}
|
||||
break;
|
||||
case MESSAGE_HANDSHAKE_RESPONSE:
|
||||
if (len == sizeof(struct message_handshake_response)) {
|
||||
result = MESSAGE_HANDSHAKE_RESPONSE;
|
||||
}
|
||||
break;
|
||||
case MESSAGE_COOKIE_REPLY:
|
||||
if (len == sizeof(struct message_cookie_reply)) {
|
||||
result = MESSAGE_COOKIE_REPLY;
|
||||
}
|
||||
break;
|
||||
case MESSAGE_TRANSPORT_DATA:
|
||||
if (len >= sizeof(struct message_transport_data) + WIREGUARD_AUTHTAG_LEN) {
|
||||
result = MESSAGE_TRANSPORT_DATA;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
wireguard_peer_t *wireguard_process_initiation_message(struct wireguard_device *device, struct message_handshake_initiation *msg) {
|
||||
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguard_process_initiation_message");
|
||||
wireguard_peer_t *ret_peer = NULL;
|
||||
wireguard_peer_t *peer = NULL;
|
||||
wireguard_handshake_t *handshake;
|
||||
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
|
||||
uint8_t chaining_key[WIREGUARD_HASH_LEN];
|
||||
uint8_t hash[WIREGUARD_HASH_LEN];
|
||||
uint8_t s[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t e[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t t[WIREGUARD_TAI64N_LEN];
|
||||
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint32_t now;
|
||||
bool rate_limit;
|
||||
bool replay;
|
||||
|
||||
// We are the responder, other end is the initiator
|
||||
|
||||
// Ci := Hash(Construction) (precalculated hash)
|
||||
memcpy(chaining_key, construction_hash, WIREGUARD_HASH_LEN);
|
||||
|
||||
// Hi := Hash(Ci || Identifier
|
||||
memcpy(hash, identifier_hash, WIREGUARD_HASH_LEN);
|
||||
|
||||
// Hi := Hash(Hi || Spubr)
|
||||
wireguard_mix_hash(hash, device->public_key, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// Ci := Kdf1(Ci, Epubi)
|
||||
wireguard_kdf1(chaining_key, chaining_key, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// msg.ephemeral := Epubi
|
||||
memcpy(e, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// Hi := Hash(Hi || msg.ephemeral)
|
||||
wireguard_mix_hash(hash, msg->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// Calculate DH(Eprivi,Spubr)
|
||||
wireguard_x25519(dh_calculation, device->private_key, e);
|
||||
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
|
||||
|
||||
// (Ci,k) := Kdf2(Ci,DH(Eprivi,Spubr))
|
||||
wireguard_kdf2(chaining_key, key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// msg.static := AEAD(k, 0, Spubi, Hi)
|
||||
if (wireguard_aead_decrypt(s, msg->enc_static, sizeof(msg->enc_static), hash, WIREGUARD_HASH_LEN, 0, key)) {
|
||||
// Hi := Hash(Hi || msg.static)
|
||||
wireguard_mix_hash(hash, msg->enc_static, sizeof(msg->enc_static));
|
||||
|
||||
peer = wireguard_peer_lookup_by_pubkey(device, s);
|
||||
if (peer) {
|
||||
handshake = &peer->handshake;
|
||||
|
||||
// (Ci,k) := Kdf2(Ci,DH(Sprivi,Spubr))
|
||||
wireguard_kdf2(chaining_key, key, chaining_key, peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// msg.timestamp := AEAD(k, 0, Timestamp(), Hi)
|
||||
if (wireguard_aead_decrypt(t, msg->enc_timestamp, sizeof(msg->enc_timestamp), hash, WIREGUARD_HASH_LEN, 0, key)) {
|
||||
// Hi := Hash(Hi || msg.timestamp)
|
||||
wireguard_mix_hash(hash, msg->enc_timestamp, sizeof(msg->enc_timestamp));
|
||||
|
||||
now = wireguard_sys_now();
|
||||
|
||||
// Check that timestamp is increasing and we haven't had too many initiations (should only get one per peer every 5 seconds max?)
|
||||
replay = (memcmp(t, peer->greatest_timestamp, WIREGUARD_TAI64N_LEN) <= 0); // tai64n is big endian so we can use memcmp to compare
|
||||
rate_limit = (peer->last_initiation_rx - now) < (1000 / MAX_INITIATIONS_PER_SECOND);
|
||||
|
||||
if (!replay && !rate_limit) {
|
||||
// Success! Copy everything to peer
|
||||
peer->last_initiation_rx = now;
|
||||
if (memcmp(t, peer->greatest_timestamp, WIREGUARD_TAI64N_LEN) > 0) {
|
||||
memcpy(peer->greatest_timestamp, t, WIREGUARD_TAI64N_LEN);
|
||||
// TODO: Need to notify if the higher layers want to persist latest timestamp/nonce somewhere
|
||||
}
|
||||
memcpy(handshake->remote_ephemeral, e, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
memcpy(handshake->hash, hash, WIREGUARD_HASH_LEN);
|
||||
memcpy(handshake->chaining_key, chaining_key, WIREGUARD_HASH_LEN);
|
||||
handshake->remote_index = msg->sender;
|
||||
handshake->valid = true;
|
||||
handshake->initiator = false;
|
||||
ret_peer = peer;
|
||||
|
||||
} else {
|
||||
// Ignore
|
||||
}
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : wireguard_process_initiation_message: failed to decrypt"));
|
||||
}
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : peer not found"));
|
||||
}
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : Failed to decrypt"));
|
||||
}
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : Bad X25519"));
|
||||
}
|
||||
|
||||
crypto_zero(key, sizeof(key));
|
||||
crypto_zero(hash, sizeof(hash));
|
||||
crypto_zero(chaining_key, sizeof(chaining_key));
|
||||
crypto_zero(dh_calculation, sizeof(dh_calculation));
|
||||
|
||||
return ret_peer;
|
||||
}
|
||||
|
||||
bool wireguard_process_handshake_response(struct wireguard_device *device, wireguard_peer_t *peer, struct message_handshake_response *src) {
|
||||
wireguard_handshake_t *handshake = &peer->handshake;
|
||||
|
||||
bool result = false;
|
||||
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
|
||||
uint8_t hash[WIREGUARD_HASH_LEN];
|
||||
uint8_t chaining_key[WIREGUARD_HASH_LEN];
|
||||
uint8_t e[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t ephemeral_private[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t static_private[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t preshared_key[WIREGUARD_SESSION_KEY_LEN];
|
||||
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t tau[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
|
||||
if (handshake->valid && handshake->initiator) {
|
||||
|
||||
memcpy(hash, handshake->hash, WIREGUARD_HASH_LEN);
|
||||
memcpy(chaining_key, handshake->chaining_key, WIREGUARD_HASH_LEN);
|
||||
memcpy(ephemeral_private, handshake->ephemeral_private, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
memcpy(preshared_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
|
||||
|
||||
// (Eprivr, Epubr) := DH-Generate()
|
||||
// Not required
|
||||
|
||||
// Cr := Kdf1(Cr,Epubr)
|
||||
wireguard_kdf1(chaining_key, chaining_key, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// msg.ephemeral := Epubr
|
||||
memcpy(e, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// Hr := Hash(Hr || msg.ephemeral)
|
||||
wireguard_mix_hash(hash, src->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// Cr := Kdf1(Cr, DH(Eprivr, Epubi))
|
||||
// Calculate DH(Eprivr, Epubi)
|
||||
wireguard_x25519(dh_calculation, ephemeral_private, e);
|
||||
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
|
||||
wireguard_kdf1(chaining_key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// Cr := Kdf1(Cr, DH(Eprivr, Spubi))
|
||||
// CalculateDH(Eprivr, Spubi)
|
||||
wireguard_x25519(dh_calculation, device->private_key, e);
|
||||
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
|
||||
wireguard_kdf1(chaining_key, chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// (Cr, t, k) := Kdf3(Cr, Q)
|
||||
wireguard_kdf3(chaining_key, tau, key, chaining_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
|
||||
|
||||
// Hr := Hash(Hr | t)
|
||||
wireguard_mix_hash(hash, tau, WIREGUARD_HASH_LEN);
|
||||
|
||||
// msg.empty := AEAD(k, 0, E, Hr)
|
||||
if (wireguard_aead_decrypt(NULL, src->enc_empty, sizeof(src->enc_empty), hash, WIREGUARD_HASH_LEN, 0, key)) {
|
||||
// Hr := Hash(Hr | msg.empty)
|
||||
// Not required as discarded
|
||||
|
||||
//Copy details to handshake
|
||||
memcpy(handshake->remote_ephemeral, e, WIREGUARD_HASH_LEN);
|
||||
memcpy(handshake->hash, hash, WIREGUARD_HASH_LEN);
|
||||
memcpy(handshake->chaining_key, chaining_key, WIREGUARD_HASH_LEN);
|
||||
handshake->remote_index = src->sender;
|
||||
|
||||
result = true;
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : Decrypt failed"));
|
||||
}
|
||||
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : X25519 fail"));
|
||||
}
|
||||
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : X25519 fail 2"));
|
||||
}
|
||||
|
||||
}
|
||||
crypto_zero(key, sizeof(key));
|
||||
crypto_zero(hash, sizeof(hash));
|
||||
crypto_zero(chaining_key, sizeof(chaining_key));
|
||||
crypto_zero(ephemeral_private, sizeof(ephemeral_private));
|
||||
crypto_zero(static_private, sizeof(static_private));
|
||||
crypto_zero(preshared_key, sizeof(preshared_key));
|
||||
crypto_zero(tau, sizeof(tau));
|
||||
|
||||
// AddLog(LOG_LEVEL_DEBUG, ">>>: OUT wireguard_process_handshake_response result %d (1==SUCCESS)");
|
||||
return result;
|
||||
}
|
||||
|
||||
bool wireguard_process_cookie_message(struct wireguard_device *device, wireguard_peer_t *peer, struct message_cookie_reply *src) {
|
||||
// AddLog(LOG_LEVEL_INFO, ">>>: wireguard_process_cookie_message");
|
||||
uint8_t cookie[WIREGUARD_COOKIE_LEN];
|
||||
bool result = false;
|
||||
|
||||
if (peer->handshake_mac1_valid) {
|
||||
|
||||
result = wireguard_xaead_decrypt(cookie, src->enc_cookie, sizeof(src->enc_cookie), peer->handshake_mac1, WIREGUARD_COOKIE_LEN, src->nonce, peer->label_cookie_key);
|
||||
|
||||
if (result) {
|
||||
// 5.4.7 Under Load: Cookie Reply Message
|
||||
// Upon receiving this message, if it is valid, the only thing the recipient of this message should do is store the cookie along with the time at which it was received
|
||||
memcpy(peer->cookie, cookie, WIREGUARD_COOKIE_LEN);
|
||||
peer->cookie_millis = wireguard_sys_now();
|
||||
peer->handshake_mac1_valid = false;
|
||||
}
|
||||
} else {
|
||||
// We didn't send any initiation packet so we shouldn't be getting a cookie reply!
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool wireguard_create_handshake_initiation(struct wireguard_device *device, wireguard_peer_t *peer, struct message_handshake_initiation *dst) {
|
||||
uint8_t timestamp[WIREGUARD_TAI64N_LEN];
|
||||
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
|
||||
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
bool result = false;
|
||||
|
||||
wireguard_handshake_t *handshake = &peer->handshake;
|
||||
|
||||
memset(dst, 0, sizeof(struct message_handshake_initiation));
|
||||
|
||||
// Ci := Hash(Construction) (precalculated hash)
|
||||
memcpy(handshake->chaining_key, construction_hash, WIREGUARD_HASH_LEN);
|
||||
|
||||
// Hi := Hash(Ci || Identifier)
|
||||
memcpy(handshake->hash, identifier_hash, WIREGUARD_HASH_LEN);
|
||||
|
||||
// Hi := Hash(Hi || Spubr)
|
||||
wireguard_mix_hash(handshake->hash, peer->public_key, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// (Eprivi, Epubi) := DH-Generate()
|
||||
wireguard_generate_private_key(handshake->ephemeral_private);
|
||||
if (wireguard_generate_public_key(dst->ephemeral, handshake->ephemeral_private)) {
|
||||
// Ci := Kdf1(Ci, Epubi)
|
||||
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// msg.ephemeral := Epubi
|
||||
// Done above - public keys is calculated into dst->ephemeral
|
||||
|
||||
// Hi := Hash(Hi || msg.ephemeral)
|
||||
wireguard_mix_hash(handshake->hash, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// Calculate DH(Eprivi,Spubr)
|
||||
wireguard_x25519(dh_calculation, handshake->ephemeral_private, peer->public_key);
|
||||
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
|
||||
|
||||
// (Ci,k) := Kdf2(Ci,DH(Eprivi,Spubr))
|
||||
wireguard_kdf2(handshake->chaining_key, key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// msg.static := AEAD(k,0,Spubi, Hi)
|
||||
wireguard_aead_encrypt(dst->enc_static, device->public_key, WIREGUARD_PUBLIC_KEY_LEN, handshake->hash, WIREGUARD_HASH_LEN, 0, key);
|
||||
|
||||
// Hi := Hash(Hi || msg.static)
|
||||
wireguard_mix_hash(handshake->hash, dst->enc_static, sizeof(dst->enc_static));
|
||||
|
||||
// (Ci,k) := Kdf2(Ci,DH(Sprivi,Spubr))
|
||||
// note DH(Sprivi,Spubr) is precomputed per peer
|
||||
wireguard_kdf2(handshake->chaining_key, key, handshake->chaining_key, peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// msg.timestamp := AEAD(k, 0, Timestamp(), Hi)
|
||||
wireguard_tai64n_now(timestamp);
|
||||
wireguard_aead_encrypt(dst->enc_timestamp, timestamp, WIREGUARD_TAI64N_LEN, handshake->hash, WIREGUARD_HASH_LEN, 0, key);
|
||||
|
||||
// Hi := Hash(Hi || msg.timestamp)
|
||||
wireguard_mix_hash(handshake->hash, dst->enc_timestamp, sizeof(dst->enc_timestamp));
|
||||
|
||||
dst->type = MESSAGE_HANDSHAKE_INITIATION;
|
||||
dst->sender = wireguard_generate_unique_index(device);
|
||||
|
||||
handshake->valid = true;
|
||||
handshake->initiator = true;
|
||||
handshake->local_index = dst->sender;
|
||||
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
// 5.4.4 Cookie MACs
|
||||
// msg.mac1 := Mac(Hash(Label-Mac1 || Spubm' ), msgA)
|
||||
// The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed
|
||||
wireguard_mac(dst->mac1, dst, (sizeof(struct message_handshake_initiation)-(2*WIREGUARD_COOKIE_LEN)), peer->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);
|
||||
|
||||
// if Lm = E or Lm ≥ 120:
|
||||
if ((peer->cookie_millis == 0) || wireguard_expired(peer->cookie_millis, COOKIE_SECRET_MAX_AGE)) {
|
||||
// msg.mac2 := 0
|
||||
crypto_zero(dst->mac2, WIREGUARD_COOKIE_LEN);
|
||||
} else {
|
||||
// msg.mac2 := Mac(Lm, msgB)
|
||||
wireguard_mac(dst->mac2, dst, (sizeof(struct message_handshake_initiation)-(WIREGUARD_COOKIE_LEN)), peer->cookie, WIREGUARD_COOKIE_LEN);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
crypto_zero(key, sizeof(key));
|
||||
crypto_zero(dh_calculation, sizeof(dh_calculation));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool wireguard_create_handshake_response(struct wireguard_device *device, wireguard_peer_t *peer, struct message_handshake_response *dst) {
|
||||
wireguard_handshake_t *handshake = &peer->handshake;
|
||||
uint8_t key[WIREGUARD_SESSION_KEY_LEN];
|
||||
uint8_t dh_calculation[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t tau[WIREGUARD_HASH_LEN];
|
||||
bool result = false;
|
||||
|
||||
memset(dst, 0, sizeof(struct message_handshake_response));
|
||||
|
||||
if (handshake->valid && !handshake->initiator) {
|
||||
|
||||
// (Eprivr, Epubr) := DH-Generate()
|
||||
wireguard_generate_private_key(handshake->ephemeral_private);
|
||||
if (wireguard_generate_public_key(dst->ephemeral, handshake->ephemeral_private)) {
|
||||
|
||||
// Cr := Kdf1(Cr,Epubr)
|
||||
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// msg.ephemeral := Epubr
|
||||
// Copied above when generated
|
||||
|
||||
// Hr := Hash(Hr || msg.ephemeral)
|
||||
wireguard_mix_hash(handshake->hash, dst->ephemeral, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// Cr := Kdf1(Cr, DH(Eprivr, Epubi))
|
||||
// Calculate DH(Eprivi,Spubr)
|
||||
wireguard_x25519(dh_calculation, handshake->ephemeral_private, handshake->remote_ephemeral);
|
||||
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
|
||||
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// Cr := Kdf1(Cr, DH(Eprivr, Spubi))
|
||||
// Calculate DH(Eprivi,Spubr)
|
||||
wireguard_x25519(dh_calculation, handshake->ephemeral_private, peer->public_key);
|
||||
if (!crypto_equal(dh_calculation, zero_key, WIREGUARD_PUBLIC_KEY_LEN)) {
|
||||
wireguard_kdf1(handshake->chaining_key, handshake->chaining_key, dh_calculation, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
|
||||
// (Cr, t, k) := Kdf3(Cr, Q)
|
||||
wireguard_kdf3(handshake->chaining_key, tau, key, handshake->chaining_key, peer->preshared_key, WIREGUARD_SESSION_KEY_LEN);
|
||||
|
||||
// Hr := Hash(Hr | t)
|
||||
wireguard_mix_hash(handshake->hash, tau, WIREGUARD_HASH_LEN);
|
||||
|
||||
// msg.empty := AEAD(k, 0, E, Hr)
|
||||
wireguard_aead_encrypt(dst->enc_empty, NULL, 0, handshake->hash, WIREGUARD_HASH_LEN, 0, key);
|
||||
|
||||
// Hr := Hash(Hr | msg.empty)
|
||||
wireguard_mix_hash(handshake->hash, dst->enc_empty, sizeof(dst->enc_empty));
|
||||
|
||||
dst->type = MESSAGE_HANDSHAKE_RESPONSE;
|
||||
dst->receiver = handshake->remote_index;
|
||||
dst->sender = wireguard_generate_unique_index(device);
|
||||
// Update handshake object too
|
||||
handshake->local_index = dst->sender;
|
||||
|
||||
result = true;
|
||||
} else {
|
||||
// Bad x25519
|
||||
}
|
||||
} else {
|
||||
// Bad x25519
|
||||
}
|
||||
|
||||
} else {
|
||||
// Failed to generate DH
|
||||
}
|
||||
}
|
||||
|
||||
if (result) {
|
||||
// 5.4.4 Cookie MACs
|
||||
// msg.mac1 := Mac(Hash(Label-Mac1 || Spubm' ), msgA)
|
||||
// The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed
|
||||
wireguard_mac(dst->mac1, dst, (sizeof(struct message_handshake_response)-(2*WIREGUARD_COOKIE_LEN)), peer->label_mac1_key, WIREGUARD_SESSION_KEY_LEN);
|
||||
|
||||
// if Lm = E or Lm ≥ 120:
|
||||
if ((peer->cookie_millis == 0) || wireguard_expired(peer->cookie_millis, COOKIE_SECRET_MAX_AGE)) {
|
||||
// msg.mac2 := 0
|
||||
crypto_zero(dst->mac2, WIREGUARD_COOKIE_LEN);
|
||||
} else {
|
||||
// msg.mac2 := Mac(Lm, msgB)
|
||||
wireguard_mac(dst->mac2, dst, (sizeof(struct message_handshake_response)-(WIREGUARD_COOKIE_LEN)), peer->cookie, WIREGUARD_COOKIE_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
crypto_zero(key, sizeof(key));
|
||||
crypto_zero(dh_calculation, sizeof(dh_calculation));
|
||||
crypto_zero(tau, sizeof(tau));
|
||||
return result;
|
||||
}
|
||||
|
||||
void wireguard_create_cookie_reply(struct wireguard_device *device, struct message_cookie_reply *dst, const uint8_t *mac1, uint32_t index, uint8_t *source_addr_port, size_t source_length) {
|
||||
uint8_t cookie[WIREGUARD_COOKIE_LEN];
|
||||
crypto_zero(dst, sizeof(struct message_cookie_reply));
|
||||
dst->type = MESSAGE_COOKIE_REPLY;
|
||||
dst->receiver = index;
|
||||
wireguard_random_bytes(dst->nonce, COOKIE_NONCE_LEN);
|
||||
generate_peer_cookie(device, cookie, source_addr_port, source_length);
|
||||
wireguard_xaead_encrypt(dst->enc_cookie, cookie, WIREGUARD_COOKIE_LEN, mac1, WIREGUARD_COOKIE_LEN, dst->nonce, device->label_cookie_key);
|
||||
}
|
||||
|
||||
bool wireguard_peer_init(struct wireguard_device *device, wireguard_peer_t *peer, const uint8_t *public_key, const uint8_t *preshared_key) {
|
||||
// Clear out structure
|
||||
memset(peer, 0, sizeof(wireguard_peer_t));
|
||||
if (device->valid) {
|
||||
// Copy across the public key into our peer structure
|
||||
memcpy(peer->public_key, public_key, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
memcpy(peer->preshared_key, preshared_key, WIREGUARD_SESSION_KEY_LEN);
|
||||
|
||||
// AddLog(LOG_LEVEL_INFO, ">>>: wireguard_peer_init pub_key %*_H priv_key %*_H peer_pub %*_H", 32, peer->public_key_dh, 32, device->private_key, 32, peer->public_key);
|
||||
if (wireguard_x25519(peer->public_key_dh, device->private_key, peer->public_key) == 0) {
|
||||
// Zero out handshake
|
||||
memset(&peer->handshake, 0, sizeof(wireguard_handshake_t));
|
||||
peer->handshake.valid = false;
|
||||
|
||||
// Zero out any cookie info - we haven't received one yet
|
||||
peer->cookie_millis = 0;
|
||||
memset(&peer->cookie, 0, WIREGUARD_COOKIE_LEN);
|
||||
|
||||
// Precompute keys to deal with mac1/2 calculation
|
||||
wireguard_mac_key(peer->label_mac1_key, peer->public_key, LABEL_MAC1, sizeof(LABEL_MAC1) - 1);
|
||||
wireguard_mac_key(peer->label_cookie_key, peer->public_key, LABEL_COOKIE, sizeof(LABEL_COOKIE) - 1);
|
||||
|
||||
peer->valid = true;
|
||||
} else {
|
||||
crypto_zero(peer->public_key_dh, WIREGUARD_PUBLIC_KEY_LEN);
|
||||
}
|
||||
}
|
||||
return peer->valid;
|
||||
}
|
||||
|
||||
bool wireguard_device_init(struct wireguard_device *device, const uint8_t *private_key) {
|
||||
// Set the private key and calculate public key from it
|
||||
memcpy(device->private_key, private_key, WIREGUARD_PRIVATE_KEY_LEN);
|
||||
// Ensure private key is correctly "clamped"
|
||||
wireguard_clamp_private_key(device->private_key);
|
||||
device->valid = wireguard_generate_public_key(device->public_key, private_key);
|
||||
if (device->valid) {
|
||||
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguard_device_init public_key %*_H", 32, device->public_key);
|
||||
generate_cookie_secret(device);
|
||||
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguard_device_init cookie secret %*_H", 32, device->cookie_secret);
|
||||
// 5.4.4 Cookie MACs - The value Hash(Label-Mac1 || Spubm' ) above can be pre-computed.
|
||||
wireguard_mac_key(device->label_mac1_key, device->public_key, LABEL_MAC1, sizeof(LABEL_MAC1) - 1);
|
||||
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguard_device_init label_mac1_key %*_H", 32, device->label_mac1_key);
|
||||
// 5.4.7 Under Load: Cookie Reply Message - The value Hash(Label-Cookie || Spubm) above can be pre-computed.
|
||||
wireguard_mac_key(device->label_cookie_key, device->public_key, LABEL_COOKIE, sizeof(LABEL_COOKIE) - 1);
|
||||
// AddLog(LOG_LEVEL_DEBUG, ">>>: wireguard_device_init label_cookie_key %*_H", 32, device->label_cookie_key);
|
||||
|
||||
} else {
|
||||
crypto_zero(device->private_key, WIREGUARD_PRIVATE_KEY_LEN);
|
||||
}
|
||||
return device->valid;
|
||||
}
|
||||
|
||||
void wireguard_encrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, wireguard_keypair_t *keypair) {
|
||||
wireguard_aead_encrypt(dst, src, src_len, NULL, 0, keypair->sending_counter, keypair->sending_key);
|
||||
keypair->sending_counter++;
|
||||
}
|
||||
|
||||
bool wireguard_decrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, uint64_t counter, wireguard_keypair_t *keypair) {
|
||||
return wireguard_aead_decrypt(dst, src, src_len, NULL, 0, counter, keypair->receiving_key);
|
||||
}
|
288
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguard.h
Normal file
288
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguard.h
Normal file
@ -0,0 +1,288 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
||||
* Copyright (c) 2025 Stephan Hadinger
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
* Author: Daniel Hope <daniel.hope@smartalock.com>
|
||||
*/
|
||||
|
||||
#ifndef _WIREGUARD_H_
|
||||
#define _WIREGUARD_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Note: these are only required for definitions in device/peer for netif, udp_pcb, ip_addr_t and u16_t
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
#include "lwip/arch.h"
|
||||
|
||||
// Platform-specific functions that need to be implemented per-platform
|
||||
#include "wireguard-platform.h"
|
||||
|
||||
// tai64n contains 64-bit seconds and 32-bit nano offset (12 bytes)
|
||||
#define WIREGUARD_TAI64N_LEN (12)
|
||||
// Auth algorithm is chacha20pol1305 which is 128bit (16 byte) authenticator
|
||||
#define WIREGUARD_AUTHTAG_LEN (16)
|
||||
// Hash algorithm is blake2s which makes 32 byte hashes
|
||||
#define WIREGUARD_HASH_LEN (32)
|
||||
// Public key algo is curve22519 which uses 32 byte keys
|
||||
#define WIREGUARD_PUBLIC_KEY_LEN (32)
|
||||
// Public key algo is curve22519 which uses 32 byte keys
|
||||
#define WIREGUARD_PRIVATE_KEY_LEN (32)
|
||||
// Symmetric session keys are chacha20/poly1305 which uses 32 byte keys
|
||||
#define WIREGUARD_SESSION_KEY_LEN (32)
|
||||
|
||||
// Timers / Limits
|
||||
#define WIREGUARD_COOKIE_LEN (16)
|
||||
#define COOKIE_SECRET_MAX_AGE (2 * 60)
|
||||
#define COOKIE_NONCE_LEN (24)
|
||||
|
||||
#define REKEY_AFTER_MESSAGES (1ULL << 60)
|
||||
#define REJECT_AFTER_MESSAGES (0xFFFFFFFFFFFFFFFFULL - (1ULL << 13))
|
||||
#define REKEY_AFTER_TIME (120)
|
||||
#define REJECT_AFTER_TIME (180)
|
||||
#define REKEY_TIMEOUT (5)
|
||||
|
||||
typedef struct wireguard_keypair {
|
||||
bool valid;
|
||||
bool initiator; // Did we initiate this session (send the initiation packet rather than sending the response packet)
|
||||
uint32_t keypair_millis;
|
||||
|
||||
uint8_t sending_key[WIREGUARD_SESSION_KEY_LEN];
|
||||
bool sending_valid;
|
||||
uint64_t sending_counter;
|
||||
|
||||
uint8_t receiving_key[WIREGUARD_SESSION_KEY_LEN];
|
||||
bool receiving_valid;
|
||||
|
||||
uint32_t last_tx;
|
||||
uint32_t last_rx;
|
||||
|
||||
uint32_t replay_bitmap;
|
||||
uint64_t replay_counter;
|
||||
|
||||
uint32_t local_index; // This is the index we generated for our end
|
||||
uint32_t remote_index; // This is the index on the other end
|
||||
} wireguard_keypair_t;
|
||||
|
||||
typedef struct wireguard_handshake {
|
||||
bool valid;
|
||||
bool initiator;
|
||||
uint32_t local_index;
|
||||
uint32_t remote_index;
|
||||
uint8_t ephemeral_private[WIREGUARD_PRIVATE_KEY_LEN];
|
||||
uint8_t remote_ephemeral[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t hash[WIREGUARD_HASH_LEN];
|
||||
uint8_t chaining_key[WIREGUARD_HASH_LEN];
|
||||
} wireguard_handshake_t;
|
||||
|
||||
typedef struct wireguard_allowed_ip {
|
||||
bool valid;
|
||||
ip_addr_t ip;
|
||||
ip_addr_t mask;
|
||||
} wireguard_allowed_ip_t;
|
||||
|
||||
typedef struct wireguard_peer {
|
||||
bool valid; // Is this peer initialised?
|
||||
bool active; // Should we be actively trying to connect?
|
||||
|
||||
// This is the configured IP of the peer (endpoint)
|
||||
ip_addr_t connect_ip;
|
||||
u16_t connect_port;
|
||||
// This is the latest received IP/port
|
||||
ip_addr_t ip;
|
||||
u16_t port;
|
||||
// keep-alive interval in seconds, 0 is disable
|
||||
uint16_t keepalive_interval;
|
||||
|
||||
struct wireguard_allowed_ip allowed_source_ips[WIREGUARD_MAX_SRC_IPS];
|
||||
|
||||
uint8_t public_key[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t preshared_key[WIREGUARD_SESSION_KEY_LEN];
|
||||
|
||||
// Precomputed DH(Sprivi,Spubr) with device private key, and peer public key
|
||||
uint8_t public_key_dh[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
|
||||
// Session keypairs
|
||||
struct wireguard_keypair curr_keypair;
|
||||
struct wireguard_keypair prev_keypair;
|
||||
struct wireguard_keypair next_keypair;
|
||||
|
||||
// 5.1 Silence is a Virtue: The responder keeps track of the greatest timestamp received per peer
|
||||
uint8_t greatest_timestamp[WIREGUARD_TAI64N_LEN];
|
||||
|
||||
// The active handshake that is happening
|
||||
struct wireguard_handshake handshake;
|
||||
|
||||
// The time of the latest completed handshake
|
||||
uint32_t latest_handshake_millis;
|
||||
|
||||
// Decrypted cookie from the responder
|
||||
uint32_t cookie_millis;
|
||||
uint8_t cookie[WIREGUARD_COOKIE_LEN];
|
||||
|
||||
// The latest mac1 we sent with initiation
|
||||
bool handshake_mac1_valid;
|
||||
uint8_t handshake_mac1[WIREGUARD_COOKIE_LEN];
|
||||
|
||||
// Precomputed keys for use in mac validation
|
||||
uint8_t label_cookie_key[WIREGUARD_SESSION_KEY_LEN];
|
||||
uint8_t label_mac1_key[WIREGUARD_SESSION_KEY_LEN];
|
||||
|
||||
// The last time we received a valid initiation message
|
||||
uint32_t last_initiation_rx;
|
||||
// The last time we sent an initiation message to this peer
|
||||
uint32_t last_initiation_tx;
|
||||
|
||||
// last_tx and last_rx of data packets
|
||||
uint32_t last_tx;
|
||||
uint32_t last_rx;
|
||||
|
||||
// We set this flag on RX/TX of packets if we think that we should initiate a new handshake
|
||||
bool send_handshake;
|
||||
} wireguard_peer_t;
|
||||
|
||||
typedef struct wireguard_device {
|
||||
// Maybe have a "Device private" member to abstract these?
|
||||
struct netif *netif;
|
||||
struct udp_pcb *udp_pcb;
|
||||
|
||||
struct netif *underlying_netif;
|
||||
|
||||
uint8_t public_key[WIREGUARD_PUBLIC_KEY_LEN];
|
||||
uint8_t private_key[WIREGUARD_PRIVATE_KEY_LEN];
|
||||
|
||||
uint8_t cookie_secret[WIREGUARD_HASH_LEN];
|
||||
uint32_t cookie_secret_millis;
|
||||
|
||||
// Precalculated
|
||||
uint8_t label_cookie_key[WIREGUARD_SESSION_KEY_LEN];
|
||||
uint8_t label_mac1_key[WIREGUARD_SESSION_KEY_LEN];
|
||||
|
||||
// List of peers associated with this device
|
||||
struct wireguard_peer peers[WIREGUARD_MAX_PEERS];
|
||||
|
||||
bool valid;
|
||||
} wireguard_device_t;
|
||||
|
||||
#define MESSAGE_INVALID 0
|
||||
#define MESSAGE_HANDSHAKE_INITIATION 1
|
||||
#define MESSAGE_HANDSHAKE_RESPONSE 2
|
||||
#define MESSAGE_COOKIE_REPLY 3
|
||||
#define MESSAGE_TRANSPORT_DATA 4
|
||||
|
||||
|
||||
// 5.4.2 First Message: Initiator to Responder
|
||||
typedef struct message_handshake_initiation {
|
||||
uint8_t type;
|
||||
uint8_t reserved[3];
|
||||
uint32_t sender;
|
||||
uint8_t ephemeral[32];
|
||||
uint8_t enc_static[32 + WIREGUARD_AUTHTAG_LEN];
|
||||
uint8_t enc_timestamp[WIREGUARD_TAI64N_LEN + WIREGUARD_AUTHTAG_LEN];
|
||||
uint8_t mac1[WIREGUARD_COOKIE_LEN];
|
||||
uint8_t mac2[WIREGUARD_COOKIE_LEN];
|
||||
} __attribute__ ((__packed__)) message_handshake_initiation_t;
|
||||
|
||||
// 5.4.3 Second Message: Responder to Initiator
|
||||
typedef struct message_handshake_response {
|
||||
uint8_t type;
|
||||
uint8_t reserved[3];
|
||||
uint32_t sender;
|
||||
uint32_t receiver;
|
||||
uint8_t ephemeral[32];
|
||||
uint8_t enc_empty[0 + WIREGUARD_AUTHTAG_LEN];
|
||||
uint8_t mac1[WIREGUARD_COOKIE_LEN];
|
||||
uint8_t mac2[WIREGUARD_COOKIE_LEN];
|
||||
} __attribute__ ((__packed__)) message_handshake_response_t;
|
||||
|
||||
// 5.4.7 Under Load: Cookie Reply Message
|
||||
typedef struct message_cookie_reply {
|
||||
uint8_t type;
|
||||
uint8_t reserved[3];
|
||||
uint32_t receiver;
|
||||
uint8_t nonce[COOKIE_NONCE_LEN];
|
||||
uint8_t enc_cookie[WIREGUARD_COOKIE_LEN + WIREGUARD_AUTHTAG_LEN];
|
||||
} __attribute__ ((__packed__)) message_cookie_reply_t;
|
||||
|
||||
// 5.4.6 Subsequent Messages: Transport Data Messages
|
||||
typedef struct message_transport_data {
|
||||
uint8_t type;
|
||||
uint8_t reserved[3];
|
||||
uint32_t receiver;
|
||||
uint8_t counter[8];
|
||||
// Followed by encrypted data
|
||||
uint8_t enc_packet[];
|
||||
} __attribute__ ((__packed__)) message_transport_data_t;
|
||||
|
||||
// Initialise the WireGuard system - need to call this before anything else
|
||||
void wireguard_init(void);
|
||||
bool wireguard_device_init(struct wireguard_device *device, const uint8_t *private_key);
|
||||
bool wireguard_peer_init(struct wireguard_device *device, wireguard_peer_t *peer, const uint8_t *public_key, const uint8_t *preshared_key);
|
||||
|
||||
wireguard_peer_t *wireguard_peer_alloc(wireguard_device_t *device);
|
||||
uint8_t wireguard_peer_index(struct wireguard_device *device, wireguard_peer_t *peer);
|
||||
wireguard_peer_t *wireguard_peer_lookup_by_pubkey(struct wireguard_device *device, uint8_t *public_key);
|
||||
wireguard_peer_t *wireguard_peer_lookup_by_peer_index(struct wireguard_device *device, uint8_t peer_index);
|
||||
wireguard_peer_t *wireguard_peer_lookup_by_receiver(struct wireguard_device *device, uint32_t receiver);
|
||||
wireguard_peer_t *wireguard_peer_lookup_by_handshake(struct wireguard_device *device, uint32_t receiver);
|
||||
|
||||
void wireguard_start_session(wireguard_peer_t *peer, bool initiator);
|
||||
|
||||
void keypair_update(wireguard_peer_t *peer, wireguard_keypair_t *received_keypair);
|
||||
void keypair_destroy(wireguard_keypair_t *keypair);
|
||||
|
||||
wireguard_keypair_t *get_peer_keypair_for_idx(wireguard_peer_t *peer, uint32_t idx);
|
||||
bool wireguard_check_replay(wireguard_keypair_t *keypair, uint64_t seq);
|
||||
|
||||
uint8_t wireguard_get_message_type(const uint8_t *data, size_t len);
|
||||
|
||||
wireguard_peer_t *wireguard_process_initiation_message(struct wireguard_device *device, struct message_handshake_initiation *msg);
|
||||
bool wireguard_process_handshake_response(struct wireguard_device *device, wireguard_peer_t *peer, struct message_handshake_response *src);
|
||||
bool wireguard_process_cookie_message(struct wireguard_device *device, wireguard_peer_t *peer, struct message_cookie_reply *src);
|
||||
|
||||
bool wireguard_create_handshake_initiation(struct wireguard_device *device, wireguard_peer_t *peer, struct message_handshake_initiation *dst);
|
||||
bool wireguard_create_handshake_response(struct wireguard_device *device, wireguard_peer_t *peer, struct message_handshake_response *dst);
|
||||
void wireguard_create_cookie_reply(struct wireguard_device *device, struct message_cookie_reply *dst, const uint8_t *mac1, uint32_t index, uint8_t *source_addr_port, size_t source_length);
|
||||
|
||||
|
||||
bool wireguard_check_mac1(struct wireguard_device *device, const uint8_t *data, size_t len, const uint8_t *mac1);
|
||||
bool wireguard_check_mac2(struct wireguard_device *device, const uint8_t *data, size_t len, uint8_t *source_addr_port, size_t source_length, const uint8_t *mac2);
|
||||
|
||||
bool wireguard_expired(uint32_t created_millis, uint32_t valid_seconds);
|
||||
|
||||
void wireguard_encrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, wireguard_keypair_t *keypair);
|
||||
bool wireguard_decrypt_packet(uint8_t *dst, const uint8_t *src, size_t src_len, uint64_t counter, wireguard_keypair_t *keypair);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _WIREGUARD_H_ */
|
1129
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguardif.cpp
Normal file
1129
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguardif.cpp
Normal file
File diff suppressed because it is too large
Load Diff
122
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguardif.h
Normal file
122
lib/lib_ssl/esp_wireguard-Tasmota/src/wireguardif.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Daniel Hope (www.floorsense.nz)
|
||||
* Copyright (c) 2023 Simone Rossetto <simros85@gmail.com>
|
||||
* Copyright (c) 2025 Stephan Hadinger
|
||||
* All rights reserved.
|
||||
*
|
||||
* 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 above 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. Neither the name of "Floorsense Ltd", "Agile Workspace Ltd" nor the names of
|
||||
* its contributors may be used to endorse or promote products derived from this
|
||||
* software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER 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.
|
||||
*
|
||||
* Author: Daniel Hope <daniel.hope@smartalock.com>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef _WIREGUARDIF_H_
|
||||
#define _WIREGUARDIF_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "esp_wireguard.h"
|
||||
|
||||
#include <time.h>
|
||||
#include "lwip/arch.h"
|
||||
#include "lwip/netif.h"
|
||||
#include "lwip/ip_addr.h"
|
||||
|
||||
// Default MTU for WireGuard is 1420 bytes
|
||||
#define WIREGUARDIF_MTU (1420)
|
||||
|
||||
#define WIREGUARDIF_DEFAULT_PORT (51820)
|
||||
|
||||
typedef struct wireguardif_init_data {
|
||||
// Required: the private key of this WireGuard network interface
|
||||
wg_key_t private_key2;
|
||||
// Required: What UDP port to listen on
|
||||
uint16_t listen_port;
|
||||
// Optional: restrict send/receive of encapsulated WireGuard traffic to this network interface only (NULL to use routing table)
|
||||
struct netif *bind_netif;
|
||||
} wireguardif_init_data_t;
|
||||
|
||||
typedef struct wireguardif_peer {
|
||||
wg_key_t public_key2;
|
||||
// Optional pre-shared key (32 bytes) - make sure this is NULL if not to be used
|
||||
wg_key_t preshared_key2;
|
||||
// tai64n of largest timestamp we have seen during handshake to avoid replays
|
||||
uint8_t greatest_timestamp[12];
|
||||
|
||||
// Allowed ip/netmask (can add additional later but at least one is required)
|
||||
ip_addr_t allowed_ip;
|
||||
ip_addr_t allowed_mask;
|
||||
|
||||
// End-point details (may be blank)
|
||||
ip_addr_t endpoint_ip;
|
||||
uint16_t endport_port;
|
||||
uint16_t keep_alive;
|
||||
} wireguardif_peer_t;
|
||||
|
||||
#define WIREGUARDIF_INVALID_INDEX (0xFF)
|
||||
|
||||
// Initialise a new WireGuard network interface (netif)
|
||||
err_t wireguardif_init(struct netif *netif);
|
||||
|
||||
// Helper to initialise the peer struct with defaults
|
||||
void wireguardif_peer_init(wireguardif_peer_t *peer);
|
||||
|
||||
// Add a new peer to the specified interface - see wireguard.h for maximum number of peers allowed
|
||||
// On success the peer_index can be used to reference this peer in future function calls
|
||||
err_t wireguardif_add_peer(struct netif *netif, wireguardif_peer_t *peer, u8_t *peer_index);
|
||||
|
||||
// Remove the given peer from the network interface
|
||||
err_t wireguardif_remove_peer(struct netif *netif, u8_t peer_index);
|
||||
|
||||
// Try and connect to the given peer
|
||||
err_t wireguardif_connect(struct netif *netif, u8_t peer_index);
|
||||
|
||||
// Stop trying to connect to the given peer
|
||||
err_t wireguardif_disconnect(struct netif *netif, u8_t peer_index);
|
||||
|
||||
// Shutdown the WireGuard interface
|
||||
void wireguardif_shutdown(struct netif *netif);
|
||||
|
||||
// Finalize the WireGuard interface after the netif is removed
|
||||
void wireguardif_fini(struct netif *netif);
|
||||
|
||||
// Is the given peer "up"? A peer is up if it has a valid session key it can communicate with
|
||||
err_t wireguardif_peer_is_up(struct netif *netif, u8_t peer_index, ip_addr_t *current_ip, u16_t *current_port);
|
||||
|
||||
// Get timestamp of latest handshake (with seconds resolution)
|
||||
// Return 0 if no handshake already done or in case of errors
|
||||
time_t wireguardif_latest_handshake(struct netif *netif, u8_t peer_index);
|
||||
|
||||
// Add ip/mask to the list of allowed ips of the given peer
|
||||
err_t wireguardif_add_allowed_ip(struct netif *netif, u8_t peer_index, const ip_addr_t& ip, const ip_addr_t& mask);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* _WIREGUARDIF_H_ */
|
@ -477,6 +477,10 @@
|
||||
// #define USE_MQTT_AZURE_DPS_PRESHAREDKEY // OPTIONAL The Preshared Key of DPS https://github.com/tasmota/docs/blob/development/docs/Azure-IoT-Central.md
|
||||
// #define USE_MQTT_AZURE_DPS_SCOPE_ENDPOINT // OPTIONAL Defaults to "https://global.azure-devices-provisioning.net/", can be changed for Azure China, Azure Germany or others.
|
||||
|
||||
// -- Wireguard VPN support ------------------------
|
||||
// VPN support since v14.6.1
|
||||
// #define USE_WIREGUARD // Enable Wireguard VPN support (ESP8266: +28k code +0.9k mem, ESP32: +22k code +0.9k mem)
|
||||
|
||||
// -- Telegram Protocol ---------------------------
|
||||
//#define USE_TELEGRAM // Support for Telegram protocol (+49k code, +7.0k mem and +4.8k additional during connection handshake)
|
||||
#define USE_TELEGRAM_FINGERPRINT "\x4E\x7F\xF5\x6D\x1E\x29\x40\x58\xAB\x84\xDE\x63\x69\x7B\xCD\xDF\x44\x2E\xD2\xF6" // Telegram api.telegram.org TLS public key fingerpring
|
||||
|
300
tasmota/tasmota_xdrv_driver/xdrv_80_wireguard_client.ino
Normal file
300
tasmota/tasmota_xdrv_driver/xdrv_80_wireguard_client.ino
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
xdrv_80_wireguard_clientc.ino - creates a VPN connection to a Wireguard site
|
||||
|
||||
Copyright (C) 2024 Stephan Hadinger
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifdef USE_WIREGUARD
|
||||
|
||||
#define XDRV_80 80
|
||||
|
||||
#include "esp_wireguard.h"
|
||||
#include "IniFile.h"
|
||||
#include "LList.h"
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
static const char WIREGUARD_CONF_FNAME[] = "/wireguard.conf";
|
||||
static const char WIREGUARD_NETMASK[] = "0.0.0.0";
|
||||
|
||||
const char kWireGuardCommands[] PROGMEM = "WG|" // Prefix
|
||||
"Connect|Stop";
|
||||
|
||||
void (* const WireGuardCommand[])(void) PROGMEM = {
|
||||
&CmndWGConnect, &CmndWGStop };
|
||||
|
||||
typedef struct {
|
||||
ip_addr_t addr;
|
||||
ip_addr_t mask;
|
||||
} allowed_ips_t;
|
||||
|
||||
struct Wireguard_t {
|
||||
bool configured = false;
|
||||
bool auto_connect = false;
|
||||
bool started = false;
|
||||
bool peer_up = false;
|
||||
int8_t peer_status = -1; // known state: -1 unknown, 0 DOWN, 1 UP
|
||||
uint32_t connected_since_utc = 0;
|
||||
String endpoint;
|
||||
LList<allowed_ips_t> allowed_ips;
|
||||
// used by lib
|
||||
wireguard_config_t config = {};
|
||||
wireguard_ctx_t ctx = {0};
|
||||
} Wireguard;
|
||||
|
||||
/*********************************************************************************************\
|
||||
* WireGuard internal lower level functions
|
||||
\*********************************************************************************************/
|
||||
|
||||
// WireguardLoadConfig
|
||||
//
|
||||
// Load configuration from INI file
|
||||
// returns 'true' if succesful
|
||||
bool WireguardLoadConfig(const char *filename) {
|
||||
if (filename == NULL) { return false; }
|
||||
if (!ffsp) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : WireGuard initialization failed, no file system"));
|
||||
return false;
|
||||
}
|
||||
File file = ffsp->open(filename, "r");
|
||||
if (!file) {
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : file '%s' not present, skipping"), filename);
|
||||
return false;
|
||||
}
|
||||
|
||||
IniFile ini(file);
|
||||
bool valconf = true;
|
||||
wireguard_config_t& config = Wireguard.config;
|
||||
valconf = true;
|
||||
valconf = valconf && ini.getValueBase64("Interface", "PrivateKey", config.private_key2, sizeof(config.private_key2));
|
||||
valconf = valconf && ini.getCIDR("Interface", "Address", &config.address2, &config.subnet);
|
||||
valconf = valconf && ini.getValueBase64("Peer", "PublicKey", config.public_key2, sizeof(config.public_key2));
|
||||
valconf = valconf && ini.getValueBase64("Peer", "PresharedKey", config.preshared_key2, sizeof(config.preshared_key2));
|
||||
valconf = valconf && ini.getDomainPort("Peer", "Endpoint", Wireguard.endpoint, Wireguard.config.port);
|
||||
// read optional NetMask
|
||||
ipaddr_aton(WIREGUARD_NETMASK, &config.netmask2);
|
||||
ini.getIPAddress("Tasmota", "Netmask", &Wireguard.config.netmask2);
|
||||
// read optional PersistentKeepalive
|
||||
ini.getValueUInt16("Peer", "PersistentKeepalive", config.persistent_keepalive);
|
||||
// read optional AutoConnect
|
||||
ini.getValueBool("Tasmota", "AutoConnect", Wireguard.auto_connect);
|
||||
// add allowedIPs
|
||||
String allowed_ips_str;
|
||||
ini.getValueString("Peer", "AllowedIPs", allowed_ips_str);
|
||||
|
||||
if (valconf) {
|
||||
// read optional AllowedIPs
|
||||
allowed_ips_t allowip;
|
||||
|
||||
while (allowed_ips_str.length() > 0) {
|
||||
int32_t comma = allowed_ips_str.indexOf(",");
|
||||
String cidr = (comma > 0) ? allowed_ips_str.substring(0, comma) : allowed_ips_str;
|
||||
cidr.trim();
|
||||
// AddLog(LOG_LEVEL_DEBUG, ">>>: allowed_ips_str '%s' comma %i cidr '%s'", allowed_ips_str.c_str(), comma, cidr.c_str());
|
||||
if (IniFile::parseCIDR(cidr, &allowip.addr, &allowip.mask)) {
|
||||
Wireguard.allowed_ips.addHead(allowip);
|
||||
} else {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : Failed to parse allowed_ips '%s', skipping"), cidr.c_str());
|
||||
}
|
||||
if (comma > 0) {
|
||||
allowed_ips_str = allowed_ips_str.substring(comma + 1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
file.close();
|
||||
if (!valconf) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : WireGuard initialization failed, invalid configuration"));
|
||||
return false;
|
||||
}
|
||||
// now parse values
|
||||
Wireguard.config.endpoint = Wireguard.endpoint.c_str();
|
||||
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : reading '%s' address:%s/%s netmask:%s endpoint:%s:%i allowed_ips_str:'%s' PersistentKeepalive:%i"),
|
||||
filename,
|
||||
IPAddress(&config.address2).toString().c_str(), IPAddress(&config.subnet).toString().c_str(),
|
||||
IPAddress(&config.netmask2).toString().c_str(),
|
||||
Wireguard.config.endpoint, Wireguard.config.port, allowed_ips_str.c_str(),
|
||||
config.persistent_keepalive);
|
||||
return true;
|
||||
}
|
||||
|
||||
// WireguardConnect
|
||||
//
|
||||
// Connect to peer
|
||||
bool WireguardConnect(void) {
|
||||
if (!Wireguard.configured || Wireguard.started) {
|
||||
return false;
|
||||
}
|
||||
|
||||
esp_err_t err = esp_wireguard_connect(&Wireguard.ctx);
|
||||
if (err == ESP_OK) {
|
||||
Wireguard.started = true;
|
||||
for (const allowed_ips_t & allowedip : Wireguard.allowed_ips) {
|
||||
err = esp_wireguard_add_allowed_ip(&Wireguard.ctx, allowedip.addr, allowedip.mask);
|
||||
if (err != ESP_OK) {
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : Failed to add allowed_ips %_I/%_I, no space left"), allowedip.addr, allowedip.mask);
|
||||
break;
|
||||
}
|
||||
AddLog(LOG_LEVEL_DEBUG, PSTR("WG : Added allowed_ips %_I/%_I"), allowedip.addr, allowedip.mask);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// WireguardStop
|
||||
//
|
||||
// Stop the current Wireguard connection
|
||||
// Do nothing if there is no connection
|
||||
void WireguardStop(void) {
|
||||
if (!Wireguard.configured || !Wireguard.started) {
|
||||
return;
|
||||
}
|
||||
// stop wireguard
|
||||
esp_wireguard_disconnect(&Wireguard.ctx);
|
||||
Wireguard.started = false;
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : Wireguard peer DOWN"));
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* WireGuard commands
|
||||
\*********************************************************************************************/
|
||||
|
||||
// Initialize Wireguard client
|
||||
void WireguardInit(void) {
|
||||
if (WireguardLoadConfig(WIREGUARD_CONF_FNAME)) {
|
||||
esp_wireguard_init(&Wireguard.config, &Wireguard.ctx);
|
||||
Wireguard.configured = true;
|
||||
} else {
|
||||
Wireguard.configured = false;
|
||||
}
|
||||
}
|
||||
|
||||
// WireGuard Connect
|
||||
void CmndWGConnect(void) {
|
||||
if (!Wireguard.configured) {
|
||||
ResponseCmndChar(PSTR("Not configured"));
|
||||
return;
|
||||
}
|
||||
if (Wireguard.started) {
|
||||
ResponseCmndChar(PSTR("Already started"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (WireguardConnect()) {
|
||||
ResponseCmndChar(PSTR("Success"));
|
||||
} else {
|
||||
ResponseCmndChar(PSTR("Failed"));
|
||||
}
|
||||
}
|
||||
|
||||
// WireGuard Stop
|
||||
void CmndWGStop(void) {
|
||||
if (!Wireguard.configured) {
|
||||
ResponseCmndChar(PSTR("Not configured"));
|
||||
return;
|
||||
}
|
||||
if (!Wireguard.started) {
|
||||
ResponseCmndChar(PSTR("Not started"));
|
||||
return;
|
||||
}
|
||||
// stop wireguard
|
||||
Wireguard.auto_connect = false; // prevent auto-reconnect when we asked for a manual stop
|
||||
WireguardStop();
|
||||
ResponseCmndChar(PSTR("Success"));
|
||||
}
|
||||
|
||||
// Loop every second
|
||||
void WireguardLoop(void) {
|
||||
if (Wireguard.started) {
|
||||
esp_err_t err = esp_wireguard_peer_is_up(&Wireguard.ctx);
|
||||
if (err == ESP_OK) {
|
||||
if (Wireguard.peer_status != 1) {
|
||||
Wireguard.peer_status = 1;
|
||||
if (Rtc.utc_time >= START_VALID_TIME) { // record the connection time only if we have a valid time
|
||||
Wireguard.connected_since_utc = Rtc.utc_time;
|
||||
}
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : Wireguard peer UP"));
|
||||
}
|
||||
// second chance, if connection happened with no time, and now time is valid
|
||||
if (Wireguard.connected_since_utc == 0 && Rtc.utc_time >= START_VALID_TIME) {
|
||||
Wireguard.connected_since_utc = Rtc.utc_time;
|
||||
}
|
||||
} else {
|
||||
if (Wireguard.peer_status != 0) {
|
||||
Wireguard.peer_status = 0;
|
||||
AddLog(LOG_LEVEL_INFO, PSTR("WG : Wireguard peer DOWN"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WireguardNetworkUpDown(bool up) {
|
||||
if (up) {
|
||||
if (!Wireguard.started && Wireguard.auto_connect) {
|
||||
WireguardConnect();
|
||||
}
|
||||
} else {
|
||||
// Network is down
|
||||
if (Wireguard.started) {
|
||||
WireguardStop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*********************************************************************************************\
|
||||
* Interface
|
||||
\*********************************************************************************************/
|
||||
|
||||
bool Xdrv80(uint32_t function) {
|
||||
bool result = false;
|
||||
if (TasmotaGlobal.no_autoexec) { return result; } // do nothing in case of bootloop
|
||||
|
||||
switch (function) {
|
||||
case FUNC_INIT:
|
||||
WireguardInit();
|
||||
break;
|
||||
case FUNC_COMMAND:
|
||||
result = DecodeCommand(kWireGuardCommands, WireGuardCommand);
|
||||
break;
|
||||
case FUNC_EVERY_SECOND:
|
||||
WireguardLoop();
|
||||
break;
|
||||
#ifdef USE_WEBSERVER
|
||||
case FUNC_WEB_STATUS:
|
||||
if (Wireguard.started && Wireguard.peer_status == 1) {
|
||||
// number of seconds since connection, or -1 if no valid time
|
||||
int32_t seconds = Wireguard.connected_since_utc ? Rtc.utc_time - Wireguard.connected_since_utc : -1;
|
||||
WSContentStatusSticker(PSTR("VPN"), seconds);
|
||||
}
|
||||
break;
|
||||
#endif // USE_WEBSERVER
|
||||
case FUNC_NETWORK_UP:
|
||||
WireguardNetworkUpDown(true);
|
||||
break;
|
||||
case FUNC_NETWORK_DOWN:
|
||||
WireguardNetworkUpDown(false);
|
||||
break;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // USE_WIREGUARD
|
Loading…
x
Reference in New Issue
Block a user