Remove LegacyCDS
This commit is contained in:
parent
13785a0936
commit
7f0a66847b
12 changed files with 28 additions and 1124 deletions
|
@ -2176,339 +2176,6 @@ Signal Desktop makes use of the following open source projects.
|
|||
|
||||
License: MIT
|
||||
|
||||
## node-forge
|
||||
|
||||
You may use the Forge project under the terms of either the BSD License or the
|
||||
GNU General Public License (GPL) Version 2.
|
||||
|
||||
The BSD License is recommended for most projects. It is simple and easy to
|
||||
understand and it places almost no restrictions on what you can do with the
|
||||
Forge project.
|
||||
|
||||
If the GPL suits your project better you are also free to use Forge under
|
||||
that license.
|
||||
|
||||
You don't have to do anything special to choose one license or the other and
|
||||
you don't have to notify anyone which license you are using. You are free to
|
||||
use this project in commercial projects as long as the copyright header is
|
||||
left intact.
|
||||
|
||||
If you are a commercial entity and use this set of libraries in your
|
||||
commercial software then reasonable payment to Digital Bazaar, if you can
|
||||
afford it, is not required but is expected and would be appreciated. If this
|
||||
library saves you time, then it's saving you money. The cost of developing
|
||||
the Forge software was on the order of several hundred hours and tens of
|
||||
thousands of dollars. We are attempting to strike a balance between helping
|
||||
the development community while not being taken advantage of by lucrative
|
||||
commercial entities for our efforts.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
New BSD License (3-clause)
|
||||
Copyright (c) 2010, Digital Bazaar, Inc.
|
||||
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 Digital Bazaar, Inc. 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 DIGITAL BAZAAR 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.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 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.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
## normalize-path
|
||||
|
||||
The MIT License (MIT)
|
||||
|
|
11
app/main.ts
11
app/main.ts
|
@ -378,16 +378,9 @@ async function prepareUrl(
|
|||
const theme = await getResolvedThemeSetting();
|
||||
|
||||
const directoryConfig = directoryConfigSchema.safeParse({
|
||||
directoryType: config.get<string | undefined>('directoryType') || 'legacy',
|
||||
directoryUrl: config.get<string | null>('directoryUrl') || undefined,
|
||||
directoryEnclaveId:
|
||||
config.get<string | null>('directoryEnclaveId') || undefined,
|
||||
directoryTrustAnchor:
|
||||
config.get<string | null>('directoryTrustAnchor') || undefined,
|
||||
directoryCDSIUrl:
|
||||
config.get<string | null>('directoryCDSIUrl') || undefined,
|
||||
directoryCDSIMRENCLAVE:
|
||||
config.get<string | null>('directoryCDSIMRENCLAVE') || undefined,
|
||||
directoryMRENCLAVE:
|
||||
config.get<string | null>('directoryMRENCLAVE') || undefined,
|
||||
});
|
||||
if (!directoryConfig.success) {
|
||||
throw new Error(
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
{
|
||||
"serverUrl": "https://chat.staging.signal.org",
|
||||
"storageUrl": "https://storage-staging.signal.org",
|
||||
"directoryType": "cdsi",
|
||||
"directoryUrl": null,
|
||||
"directoryEnclaveId": null,
|
||||
"directoryTrustAnchor": null,
|
||||
"directoryCDSIUrl": "https://cdsi.staging.signal.org",
|
||||
"directoryCDSIMRENCLAVE": "ef4787a56a154ac6d009138cac17155acd23cfe4329281252365dd7c252e7fbf",
|
||||
"directoryUrl": "https://cdsi.staging.signal.org",
|
||||
"directoryMRENCLAVE": "ef4787a56a154ac6d009138cac17155acd23cfe4329281252365dd7c252e7fbf",
|
||||
"cdn": {
|
||||
"0": "https://cdn-staging.signal.org",
|
||||
"2": "https://cdn2-staging.signal.org"
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
{
|
||||
"serverUrl": "https://chat.signal.org",
|
||||
"storageUrl": "https://storage.signal.org",
|
||||
"directoryType": "mirrored-cdsi",
|
||||
"directoryUrl": "https://api.directory.signal.org",
|
||||
"directoryEnclaveId": "74778bb0f93ae1f78c26e67152bab0bbeb693cd56d1bb9b4e9244157acc58081",
|
||||
"directoryTrustAnchor": "-----BEGIN CERTIFICATE-----\nMIIFSzCCA7OgAwIBAgIJANEHdl0yo7CUMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNV\nBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwLU2FudGEgQ2xhcmExGjAYBgNV\nBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQDDCdJbnRlbCBTR1ggQXR0ZXN0\nYXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwIBcNMTYxMTE0MTUzNzMxWhgPMjA0OTEy\nMzEyMzU5NTlaMH4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEUMBIGA1UEBwwL\nU2FudGEgQ2xhcmExGjAYBgNVBAoMEUludGVsIENvcnBvcmF0aW9uMTAwLgYDVQQD\nDCdJbnRlbCBTR1ggQXR0ZXN0YXRpb24gUmVwb3J0IFNpZ25pbmcgQ0EwggGiMA0G\nCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCfPGR+tXc8u1EtJzLA10Feu1Wg+p7e\nLmSRmeaCHbkQ1TF3Nwl3RmpqXkeGzNLd69QUnWovYyVSndEMyYc3sHecGgfinEeh\nrgBJSEdsSJ9FpaFdesjsxqzGRa20PYdnnfWcCTvFoulpbFR4VBuXnnVLVzkUvlXT\nL/TAnd8nIZk0zZkFJ7P5LtePvykkar7LcSQO85wtcQe0R1Raf/sQ6wYKaKmFgCGe\nNpEJUmg4ktal4qgIAxk+QHUxQE42sxViN5mqglB0QJdUot/o9a/V/mMeH8KvOAiQ\nbyinkNndn+Bgk5sSV5DFgF0DffVqmVMblt5p3jPtImzBIH0QQrXJq39AT8cRwP5H\nafuVeLHcDsRp6hol4P+ZFIhu8mmbI1u0hH3W/0C2BuYXB5PC+5izFFh/nP0lc2Lf\n6rELO9LZdnOhpL1ExFOq9H/B8tPQ84T3Sgb4nAifDabNt/zu6MmCGo5U8lwEFtGM\nRoOaX4AS+909x00lYnmtwsDVWv9vBiJCXRsCAwEAAaOByTCBxjBgBgNVHR8EWTBX\nMFWgU6BRhk9odHRwOi8vdHJ1c3RlZHNlcnZpY2VzLmludGVsLmNvbS9jb250ZW50\nL0NSTC9TR1gvQXR0ZXN0YXRpb25SZXBvcnRTaWduaW5nQ0EuY3JsMB0GA1UdDgQW\nBBR4Q3t2pn680K9+QjfrNXw7hwFRPDAfBgNVHSMEGDAWgBR4Q3t2pn680K9+Qjfr\nNXw7hwFRPDAOBgNVHQ8BAf8EBAMCAQYwEgYDVR0TAQH/BAgwBgEB/wIBADANBgkq\nhkiG9w0BAQsFAAOCAYEAeF8tYMXICvQqeXYQITkV2oLJsp6J4JAqJabHWxYJHGir\nIEqucRiJSSx+HjIJEUVaj8E0QjEud6Y5lNmXlcjqRXaCPOqK0eGRz6hi+ripMtPZ\nsFNaBwLQVV905SDjAzDzNIDnrcnXyB4gcDFCvwDFKKgLRjOB/WAqgscDUoGq5ZVi\nzLUzTqiQPmULAQaB9c6Oti6snEFJiCQ67JLyW/E83/frzCmO5Ru6WjU4tmsmy8Ra\nUd4APK0wZTGtfPXU7w+IBdG5Ez0kE1qzxGQaL4gINJ1zMyleDnbuS8UicjJijvqA\n152Sq049ESDz+1rRGc2NVEqh1KaGXmtXvqxXcTB+Ljy5Bw2ke0v8iGngFBPqCTVB\n3op5KBG3RjbF6RRSzwzuWfL7QErNC8WEy5yDVARzTA5+xmBc388v9Dm21HGfcC8O\nDD+gT9sSpssq0ascmvH49MOgjt1yoysLtdCtJW/9FZpoOypaHx0R+mJTLwPXVMrv\nDaVzWh5aiEx+idkSGMnX\n-----END CERTIFICATE-----\n",
|
||||
"directoryCDSIUrl": "https://cdsi.signal.org",
|
||||
"directoryCDSIMRENCLAVE": "ef4787a56a154ac6d009138cac17155acd23cfe4329281252365dd7c252e7fbf",
|
||||
"directoryUrl": "https://cdsi.signal.org",
|
||||
"directoryMRENCLAVE": "ef4787a56a154ac6d009138cac17155acd23cfe4329281252365dd7c252e7fbf",
|
||||
"cdn": {
|
||||
"0": "https://cdn.signal.org",
|
||||
"2": "https://cdn2.signal.org"
|
||||
|
|
|
@ -136,7 +136,6 @@
|
|||
"mp4box": "0.5.2",
|
||||
"mustache": "2.3.0",
|
||||
"node-fetch": "2.6.7",
|
||||
"node-forge": "1.3.0",
|
||||
"normalize-path": "3.0.0",
|
||||
"p-map": "2.1.0",
|
||||
"p-props": "4.0.0",
|
||||
|
@ -239,7 +238,6 @@
|
|||
"@types/mustache": "4.1.2",
|
||||
"@types/node": "16.11.29",
|
||||
"@types/node-fetch": "2.5.7",
|
||||
"@types/node-forge": "0.9.5",
|
||||
"@types/normalize-path": "3.0.0",
|
||||
"@types/pify": "3.0.2",
|
||||
"@types/quill": "1.3.10",
|
||||
|
|
69
ts/Crypto.ts
69
ts/Crypto.ts
|
@ -2,7 +2,6 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { Buffer } from 'buffer';
|
||||
import pProps from 'p-props';
|
||||
import Long from 'long';
|
||||
import { HKDF } from '@signalapp/libsignal-client';
|
||||
|
||||
|
@ -362,74 +361,6 @@ export function getBytes(
|
|||
return data.subarray(start, start + n);
|
||||
}
|
||||
|
||||
function _getMacAndData(ciphertext: Uint8Array) {
|
||||
const dataLength = ciphertext.byteLength - MAC_LENGTH;
|
||||
const data = getBytes(ciphertext, 0, dataLength);
|
||||
const mac = getBytes(ciphertext, dataLength, MAC_LENGTH);
|
||||
|
||||
return { data, mac };
|
||||
}
|
||||
|
||||
export async function encryptCdsDiscoveryRequest(
|
||||
attestations: {
|
||||
[key: string]: { clientKey: Uint8Array; requestId: Uint8Array };
|
||||
},
|
||||
phoneNumbers: ReadonlyArray<string>
|
||||
): Promise<Record<string, unknown>> {
|
||||
const nonce = getRandomBytes(32);
|
||||
const numbersArray = Buffer.concat(
|
||||
phoneNumbers.map(number => {
|
||||
// Long.fromString handles numbers with or without a leading '+'
|
||||
return new Uint8Array(Long.fromString(number).toBytesBE());
|
||||
})
|
||||
);
|
||||
|
||||
// We've written to the array, so offset === byteLength; we need to reset it. Then we'll
|
||||
// have access to everything in the array when we generate an Uint8Array from it.
|
||||
const queryDataPlaintext = Bytes.concatenate([nonce, numbersArray]);
|
||||
|
||||
const queryDataKey = getRandomBytes(32);
|
||||
const commitment = sha256(queryDataPlaintext);
|
||||
const iv = getRandomBytes(12);
|
||||
const queryDataCiphertext = encryptAesGcm(
|
||||
queryDataKey,
|
||||
iv,
|
||||
queryDataPlaintext
|
||||
);
|
||||
const { data: queryDataCiphertextData, mac: queryDataCiphertextMac } =
|
||||
_getMacAndData(queryDataCiphertext);
|
||||
|
||||
const envelopes = await pProps(
|
||||
attestations,
|
||||
async ({ clientKey, requestId }) => {
|
||||
const envelopeIv = getRandomBytes(12);
|
||||
const ciphertext = encryptAesGcm(
|
||||
clientKey,
|
||||
envelopeIv,
|
||||
queryDataKey,
|
||||
requestId
|
||||
);
|
||||
const { data, mac } = _getMacAndData(ciphertext);
|
||||
|
||||
return {
|
||||
requestId: Bytes.toBase64(requestId),
|
||||
data: Bytes.toBase64(data),
|
||||
iv: Bytes.toBase64(envelopeIv),
|
||||
mac: Bytes.toBase64(mac),
|
||||
};
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
addressCount: phoneNumbers.length,
|
||||
commitment: Bytes.toBase64(commitment),
|
||||
data: Bytes.toBase64(queryDataCiphertextData),
|
||||
iv: Bytes.toBase64(iv),
|
||||
mac: Bytes.toBase64(queryDataCiphertextMac),
|
||||
envelopes,
|
||||
};
|
||||
}
|
||||
|
||||
export function bytesToUuid(bytes: Uint8Array): undefined | UUIDStringType {
|
||||
if (bytes.byteLength !== UUID_BYTE_SIZE) {
|
||||
log.warn(
|
||||
|
|
|
@ -15,10 +15,7 @@ import { getCountryCode } from './types/PhoneNumber';
|
|||
export type ConfigKeyType =
|
||||
| 'desktop.announcementGroup'
|
||||
| 'desktop.calling.audioLevelForSpeaking'
|
||||
| 'desktop.cdsi'
|
||||
| 'desktop.cdsi.beta'
|
||||
| 'desktop.cdsi.returnAcisWithoutUaks'
|
||||
| 'desktop.cdsi.mirroring'
|
||||
| 'desktop.clientExpiration'
|
||||
| 'desktop.groupCallOutboundRing'
|
||||
| 'desktop.internalUser'
|
||||
|
|
|
@ -40,14 +40,7 @@ import * as linkPreviewFetch from '../linkPreviews/linkPreviewFetch';
|
|||
import { isBadgeImageFileUrlValid } from '../badges/isBadgeImageFileUrlValid';
|
||||
|
||||
import { SocketManager } from './SocketManager';
|
||||
import type {
|
||||
CDSAuthType,
|
||||
CDSRequestOptionsType,
|
||||
CDSResponseType,
|
||||
} from './cds/Types.d';
|
||||
import type { CDSBase } from './cds/CDSBase';
|
||||
import { LegacyCDS } from './cds/LegacyCDS';
|
||||
import type { LegacyCDSPutAttestationResponseType } from './cds/LegacyCDS';
|
||||
import type { CDSAuthType, CDSResponseType } from './cds/Types.d';
|
||||
import { CDSI } from './cds/CDSI';
|
||||
import type WebSocketResource from './WebsocketResources';
|
||||
import { SignalService as Proto } from '../protobuf';
|
||||
|
@ -63,12 +56,6 @@ import type {
|
|||
import { handleStatusCode, translateError } from './Utils';
|
||||
import * as log from '../logging/log';
|
||||
import { maybeParseUrl } from '../util/url';
|
||||
import {
|
||||
ToastInternalError,
|
||||
ToastInternalErrorKind,
|
||||
} from '../components/ToastInternalError';
|
||||
import { showToast } from '../util/showToast';
|
||||
import { isProduction } from '../util/version';
|
||||
|
||||
// Note: this will break some code that expects to be able to use err.response when a
|
||||
// web request fails, because it will force it to text. But it is very useful for
|
||||
|
@ -738,8 +725,6 @@ export type CdsLookupOptionsType = Readonly<{
|
|||
acis?: ReadonlyArray<UUIDStringType>;
|
||||
accessKeys?: ReadonlyArray<string>;
|
||||
returnAcisWithoutUaks?: boolean;
|
||||
isLegacy: boolean;
|
||||
isMirroring: boolean;
|
||||
}>;
|
||||
|
||||
type GetProfileCommonOptionsType = Readonly<
|
||||
|
@ -1126,117 +1111,25 @@ export function initialize({
|
|||
socketManager.authenticate({ username, password });
|
||||
}
|
||||
|
||||
const {
|
||||
directoryType,
|
||||
directoryUrl,
|
||||
directoryEnclaveId,
|
||||
directoryTrustAnchor,
|
||||
} = directoryConfig;
|
||||
const { directoryUrl, directoryMRENCLAVE } = directoryConfig;
|
||||
|
||||
let legacyCDS: LegacyCDS | undefined;
|
||||
let cds: CDSBase;
|
||||
if (directoryType === 'legacy' || directoryType === 'mirrored-cdsi') {
|
||||
legacyCDS = new LegacyCDS({
|
||||
logger: log,
|
||||
directoryEnclaveId,
|
||||
directoryTrustAnchor,
|
||||
proxyUrl,
|
||||
const cds = new CDSI({
|
||||
logger: log,
|
||||
proxyUrl,
|
||||
|
||||
async putAttestation(auth, publicKey) {
|
||||
const data = JSON.stringify({
|
||||
clientPublic: Bytes.toBase64(publicKey),
|
||||
iasVersion: 4,
|
||||
});
|
||||
const result = (await _outerAjax(null, {
|
||||
certificateAuthority,
|
||||
type: 'PUT',
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
host: directoryUrl,
|
||||
path: `${URL_CALLS.attestation}/${directoryEnclaveId}`,
|
||||
user: auth.username,
|
||||
password: auth.password,
|
||||
proxyUrl,
|
||||
responseType: 'jsonwithdetails',
|
||||
data,
|
||||
timeout: 30000,
|
||||
version,
|
||||
})) as JSONWithDetailsType<LegacyCDSPutAttestationResponseType>;
|
||||
url: directoryUrl,
|
||||
mrenclave: directoryMRENCLAVE,
|
||||
certificateAuthority,
|
||||
version,
|
||||
|
||||
const { response, data: responseBody } = result;
|
||||
|
||||
const cookie = response.headers.get('set-cookie') ?? undefined;
|
||||
|
||||
return { cookie, responseBody };
|
||||
},
|
||||
|
||||
async fetchDiscoveryData(auth, data, cookie) {
|
||||
const response = (await _outerAjax(null, {
|
||||
certificateAuthority,
|
||||
type: 'PUT',
|
||||
headers: cookie
|
||||
? {
|
||||
cookie,
|
||||
}
|
||||
: undefined,
|
||||
contentType: 'application/json; charset=utf-8',
|
||||
host: directoryUrl,
|
||||
path: `${URL_CALLS.discovery}/${directoryEnclaveId}`,
|
||||
user: auth.username,
|
||||
password: auth.password,
|
||||
proxyUrl,
|
||||
responseType: 'json',
|
||||
timeout: 30000,
|
||||
data: JSON.stringify(data),
|
||||
version,
|
||||
})) as {
|
||||
requestId: string;
|
||||
iv: string;
|
||||
data: string;
|
||||
mac: string;
|
||||
};
|
||||
|
||||
return {
|
||||
requestId: Bytes.fromBase64(response.requestId),
|
||||
iv: Bytes.fromBase64(response.iv),
|
||||
data: Bytes.fromBase64(response.data),
|
||||
mac: Bytes.fromBase64(response.mac),
|
||||
};
|
||||
},
|
||||
|
||||
async getAuth() {
|
||||
return (await _ajax({
|
||||
call: 'directoryAuth',
|
||||
httpType: 'GET',
|
||||
responseType: 'json',
|
||||
})) as CDSAuthType;
|
||||
},
|
||||
});
|
||||
|
||||
if (directoryType === 'legacy') {
|
||||
cds = legacyCDS;
|
||||
}
|
||||
}
|
||||
if (directoryType === 'cdsi' || directoryType === 'mirrored-cdsi') {
|
||||
const { directoryCDSIUrl, directoryCDSIMRENCLAVE } = directoryConfig;
|
||||
|
||||
cds = new CDSI({
|
||||
logger: log,
|
||||
proxyUrl,
|
||||
|
||||
url: directoryCDSIUrl,
|
||||
mrenclave: directoryCDSIMRENCLAVE,
|
||||
certificateAuthority,
|
||||
version,
|
||||
|
||||
async getAuth() {
|
||||
return (await _ajax({
|
||||
call: 'directoryAuthV2',
|
||||
httpType: 'GET',
|
||||
responseType: 'json',
|
||||
})) as CDSAuthType;
|
||||
},
|
||||
});
|
||||
}
|
||||
async getAuth() {
|
||||
return (await _ajax({
|
||||
call: 'directoryAuthV2',
|
||||
httpType: 'GET',
|
||||
responseType: 'json',
|
||||
})) as CDSAuthType;
|
||||
},
|
||||
});
|
||||
|
||||
let fetchForLinkPreviews: linkPreviewFetch.FetchFn;
|
||||
if (proxyUrl) {
|
||||
|
@ -2936,74 +2829,18 @@ export function initialize({
|
|||
return socketManager.getProvisioningResource(handler);
|
||||
}
|
||||
|
||||
async function mirroredCdsLookup(
|
||||
requestOptions: CDSRequestOptionsType,
|
||||
expectedMapPromise: Promise<CDSResponseType>
|
||||
): Promise<void> {
|
||||
try {
|
||||
log.info('cdsLookup: sending mirrored request');
|
||||
const actualMap = await cds.request(requestOptions);
|
||||
|
||||
const expectedMap = await expectedMapPromise;
|
||||
let matched = 0;
|
||||
let warnings = 0;
|
||||
for (const [e164, { aci }] of actualMap) {
|
||||
if (!aci) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const expectedACI = expectedMap.get(e164)?.aci;
|
||||
if (expectedACI === aci) {
|
||||
matched += 1;
|
||||
} else {
|
||||
warnings += 1;
|
||||
log.warn(
|
||||
`cdsLookup: mirrored request has aci=${aci} for ${e164}, while ` +
|
||||
`expected aci=${expectedACI}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (warnings !== 0 && !isProduction(window.getVersion())) {
|
||||
log.info('cdsLookup: showing error toast');
|
||||
showToast(ToastInternalError, {
|
||||
kind: ToastInternalErrorKind.CDSMirroringError,
|
||||
onShowDebugLog: () => window.showDebugLog(),
|
||||
});
|
||||
}
|
||||
|
||||
log.info(`cdsLookup: mirrored request success, matched=${matched}`);
|
||||
} catch (error) {
|
||||
log.error('cdsLookup: mirrored request error', toLogFormat(error));
|
||||
}
|
||||
}
|
||||
|
||||
async function cdsLookup({
|
||||
e164s,
|
||||
acis = [],
|
||||
accessKeys = [],
|
||||
returnAcisWithoutUaks,
|
||||
isLegacy,
|
||||
isMirroring,
|
||||
}: CdsLookupOptionsType): Promise<CDSResponseType> {
|
||||
const requestOptions = {
|
||||
return cds.request({
|
||||
e164s,
|
||||
acis,
|
||||
accessKeys,
|
||||
returnAcisWithoutUaks,
|
||||
};
|
||||
if (!isLegacy || !legacyCDS) {
|
||||
return cds.request(requestOptions);
|
||||
}
|
||||
|
||||
const legacyRequest = legacyCDS.request(requestOptions);
|
||||
|
||||
if (legacyCDS !== cds && isMirroring) {
|
||||
// Intentionally not awaiting
|
||||
mirroredCdsLookup(requestOptions, legacyRequest);
|
||||
}
|
||||
|
||||
return legacyRequest;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,459 +0,0 @@
|
|||
// Copyright 2020-2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
/* eslint-disable no-bitwise */
|
||||
|
||||
import pProps from 'p-props';
|
||||
import { compact } from 'lodash';
|
||||
import Long from 'long';
|
||||
import { createVerify } from 'crypto';
|
||||
import { pki } from 'node-forge';
|
||||
|
||||
import {
|
||||
constantTimeEqual,
|
||||
decryptAesGcm,
|
||||
deriveSecrets,
|
||||
encryptCdsDiscoveryRequest,
|
||||
splitUuids,
|
||||
} from '../../Crypto';
|
||||
import { calculateAgreement, generateKeyPair } from '../../Curve';
|
||||
import * as Bytes from '../../Bytes';
|
||||
import { UUID } from '../../types/UUID';
|
||||
import type { CDSBaseOptionsType } from './CDSBase';
|
||||
import { CDSBase } from './CDSBase';
|
||||
import type {
|
||||
CDSRequestOptionsType,
|
||||
CDSResponseType,
|
||||
CDSAuthType,
|
||||
CDSResponseEntryType,
|
||||
} from './Types.d';
|
||||
|
||||
export type LegacyCDSPutAttestationResponseType = Readonly<{
|
||||
attestations: Record<
|
||||
string,
|
||||
{
|
||||
ciphertext: string;
|
||||
iv: string;
|
||||
quote: string;
|
||||
serverEphemeralPublic: string;
|
||||
serverStaticPublic: string;
|
||||
signature: string;
|
||||
signatureBody: string;
|
||||
tag: string;
|
||||
certificates: string;
|
||||
}
|
||||
>;
|
||||
}>;
|
||||
|
||||
export type LegacyCDSPutAttestationResultType = Readonly<{
|
||||
cookie?: string;
|
||||
responseBody: LegacyCDSPutAttestationResponseType;
|
||||
}>;
|
||||
|
||||
export type LegacyCDSDiscoveryResponseType = Readonly<{
|
||||
requestId: Uint8Array;
|
||||
iv: Uint8Array;
|
||||
data: Uint8Array;
|
||||
mac: Uint8Array;
|
||||
}>;
|
||||
|
||||
export type LegacyCDSOptionsType = Readonly<{
|
||||
directoryEnclaveId: string;
|
||||
directoryTrustAnchor: string;
|
||||
|
||||
putAttestation: (
|
||||
auth: CDSAuthType,
|
||||
publicKey: Uint8Array
|
||||
) => Promise<LegacyCDSPutAttestationResultType>;
|
||||
fetchDiscoveryData: (
|
||||
auth: CDSAuthType,
|
||||
data: Record<string, unknown>,
|
||||
cookie?: string
|
||||
) => Promise<LegacyCDSDiscoveryResponseType>;
|
||||
}> &
|
||||
CDSBaseOptionsType;
|
||||
|
||||
type AttestationMapType = Readonly<{
|
||||
cookie?: string;
|
||||
attestations: Record<
|
||||
string,
|
||||
Readonly<{
|
||||
clientKey: Uint8Array;
|
||||
serverKey: Uint8Array;
|
||||
requestId: Uint8Array;
|
||||
}>
|
||||
>;
|
||||
}>;
|
||||
|
||||
type SgxConstantsType = {
|
||||
SGX_FLAGS_INITTED: Long;
|
||||
SGX_FLAGS_DEBUG: Long;
|
||||
SGX_FLAGS_MODE64BIT: Long;
|
||||
SGX_FLAGS_PROVISION_KEY: Long;
|
||||
SGX_FLAGS_EINITTOKEN_KEY: Long;
|
||||
SGX_FLAGS_RESERVED: Long;
|
||||
SGX_XFRM_LEGACY: Long;
|
||||
SGX_XFRM_AVX: Long;
|
||||
SGX_XFRM_RESERVED: Long;
|
||||
};
|
||||
|
||||
let sgxConstantCache: SgxConstantsType | null = null;
|
||||
|
||||
function makeLong(value: string): Long {
|
||||
return Long.fromString(value);
|
||||
}
|
||||
function getSgxConstants() {
|
||||
if (sgxConstantCache) {
|
||||
return sgxConstantCache;
|
||||
}
|
||||
|
||||
sgxConstantCache = {
|
||||
SGX_FLAGS_INITTED: makeLong('x0000000000000001L'),
|
||||
SGX_FLAGS_DEBUG: makeLong('x0000000000000002L'),
|
||||
SGX_FLAGS_MODE64BIT: makeLong('x0000000000000004L'),
|
||||
SGX_FLAGS_PROVISION_KEY: makeLong('x0000000000000004L'),
|
||||
SGX_FLAGS_EINITTOKEN_KEY: makeLong('x0000000000000004L'),
|
||||
SGX_FLAGS_RESERVED: makeLong('xFFFFFFFFFFFFFFC8L'),
|
||||
SGX_XFRM_LEGACY: makeLong('x0000000000000003L'),
|
||||
SGX_XFRM_AVX: makeLong('x0000000000000006L'),
|
||||
SGX_XFRM_RESERVED: makeLong('xFFFFFFFFFFFFFFF8L'),
|
||||
};
|
||||
|
||||
return sgxConstantCache;
|
||||
}
|
||||
|
||||
export class LegacyCDS extends CDSBase<LegacyCDSOptionsType> {
|
||||
public override async request({
|
||||
e164s,
|
||||
}: CDSRequestOptionsType): Promise<CDSResponseType> {
|
||||
const directoryAuth = await this.getAuth();
|
||||
const attestationResult = await this.putAttestation(directoryAuth);
|
||||
|
||||
// Encrypt data for discovery
|
||||
const data = await encryptCdsDiscoveryRequest(
|
||||
attestationResult.attestations,
|
||||
e164s
|
||||
);
|
||||
const { cookie } = attestationResult;
|
||||
|
||||
// Send discovery request
|
||||
const discoveryResponse = await this.options.fetchDiscoveryData(
|
||||
directoryAuth,
|
||||
data,
|
||||
cookie
|
||||
);
|
||||
|
||||
const returnedAttestation = Object.values(
|
||||
attestationResult.attestations
|
||||
).find(at => constantTimeEqual(at.requestId, discoveryResponse.requestId));
|
||||
if (!returnedAttestation) {
|
||||
throw new Error('No known attestations returned from CDS');
|
||||
}
|
||||
|
||||
// Decrypt discovery response
|
||||
const decryptedDiscoveryData = decryptAesGcm(
|
||||
returnedAttestation.serverKey,
|
||||
discoveryResponse.iv,
|
||||
Bytes.concatenate([discoveryResponse.data, discoveryResponse.mac])
|
||||
);
|
||||
|
||||
// Process and return result
|
||||
const uuids = splitUuids(decryptedDiscoveryData);
|
||||
|
||||
if (uuids.length !== e164s.length) {
|
||||
throw new Error(
|
||||
'Returned set of UUIDs did not match returned set of e164s!'
|
||||
);
|
||||
}
|
||||
|
||||
const result = new Map<string, CDSResponseEntryType>();
|
||||
|
||||
for (const [i, e164] of e164s.entries()) {
|
||||
const uuid = uuids[i];
|
||||
result.set(e164, {
|
||||
aci: uuid ? UUID.cast(uuid) : undefined,
|
||||
pni: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Private
|
||||
//
|
||||
|
||||
private async putAttestation(auth: CDSAuthType): Promise<AttestationMapType> {
|
||||
const { privKey, pubKey } = generateKeyPair();
|
||||
// Remove first "key type" byte from public key
|
||||
const slicedPubKey = pubKey.slice(1);
|
||||
// Do request
|
||||
const { cookie, responseBody } = await this.options.putAttestation(
|
||||
auth,
|
||||
slicedPubKey
|
||||
);
|
||||
|
||||
const attestationsLength = Object.keys(responseBody.attestations).length;
|
||||
if (attestationsLength > 3) {
|
||||
throw new Error(
|
||||
'Got more than three attestations from the Contact Discovery Service'
|
||||
);
|
||||
}
|
||||
if (attestationsLength < 1) {
|
||||
throw new Error('Got no attestations from the Contact Discovery Service');
|
||||
}
|
||||
|
||||
// Decode response
|
||||
return {
|
||||
cookie,
|
||||
attestations: await pProps(
|
||||
responseBody.attestations,
|
||||
async attestation => {
|
||||
const decoded = {
|
||||
...attestation,
|
||||
ciphertext: Bytes.fromBase64(attestation.ciphertext),
|
||||
iv: Bytes.fromBase64(attestation.iv),
|
||||
quote: Bytes.fromBase64(attestation.quote),
|
||||
serverEphemeralPublic: Bytes.fromBase64(
|
||||
attestation.serverEphemeralPublic
|
||||
),
|
||||
serverStaticPublic: Bytes.fromBase64(
|
||||
attestation.serverStaticPublic
|
||||
),
|
||||
signature: Bytes.fromBase64(attestation.signature),
|
||||
tag: Bytes.fromBase64(attestation.tag),
|
||||
};
|
||||
|
||||
// Validate response
|
||||
this.validateAttestationQuote(decoded);
|
||||
validateAttestationSignatureBody(
|
||||
JSON.parse(decoded.signatureBody),
|
||||
attestation.quote
|
||||
);
|
||||
await this.validateAttestationSignature(
|
||||
decoded.signature,
|
||||
decoded.signatureBody,
|
||||
decoded.certificates
|
||||
);
|
||||
|
||||
// Derive key
|
||||
const ephemeralToEphemeral = calculateAgreement(
|
||||
decoded.serverEphemeralPublic,
|
||||
privKey
|
||||
);
|
||||
const ephemeralToStatic = calculateAgreement(
|
||||
decoded.serverStaticPublic,
|
||||
privKey
|
||||
);
|
||||
const masterSecret = Bytes.concatenate([
|
||||
ephemeralToEphemeral,
|
||||
ephemeralToStatic,
|
||||
]);
|
||||
const publicKeys = Bytes.concatenate([
|
||||
slicedPubKey,
|
||||
decoded.serverEphemeralPublic,
|
||||
decoded.serverStaticPublic,
|
||||
]);
|
||||
const [clientKey, serverKey] = deriveSecrets(
|
||||
masterSecret,
|
||||
publicKeys,
|
||||
new Uint8Array(0)
|
||||
);
|
||||
|
||||
// Decrypt ciphertext into requestId
|
||||
const requestId = decryptAesGcm(
|
||||
serverKey,
|
||||
decoded.iv,
|
||||
Bytes.concatenate([decoded.ciphertext, decoded.tag])
|
||||
);
|
||||
|
||||
return {
|
||||
clientKey,
|
||||
serverKey,
|
||||
requestId,
|
||||
};
|
||||
}
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
private async validateAttestationSignature(
|
||||
signature: Uint8Array,
|
||||
signatureBody: string,
|
||||
certificates: string
|
||||
) {
|
||||
const CERT_PREFIX = '-----BEGIN CERTIFICATE-----';
|
||||
const pem = compact(
|
||||
certificates.split(CERT_PREFIX).map(match => {
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return `${CERT_PREFIX}${match}`;
|
||||
})
|
||||
);
|
||||
if (pem.length < 2) {
|
||||
throw new Error(
|
||||
`validateAttestationSignature: Expect two or more entries; got ${pem.length}`
|
||||
);
|
||||
}
|
||||
|
||||
const verify = createVerify('RSA-SHA256');
|
||||
verify.update(Buffer.from(Bytes.fromString(signatureBody)));
|
||||
const isValid = verify.verify(pem[0], Buffer.from(signature));
|
||||
if (!isValid) {
|
||||
throw new Error('Validation of signature across signatureBody failed!');
|
||||
}
|
||||
|
||||
const caStore = pki.createCaStore([this.options.directoryTrustAnchor]);
|
||||
const chain = compact(pem.map(cert => pki.certificateFromPem(cert)));
|
||||
const isChainValid = pki.verifyCertificateChain(caStore, chain);
|
||||
if (!isChainValid) {
|
||||
throw new Error('Validation of certificate chain failed!');
|
||||
}
|
||||
|
||||
const leafCert = chain[0];
|
||||
const fieldCN = leafCert.subject.getField('CN');
|
||||
if (!fieldCN || fieldCN.value !== 'Intel SGX Attestation Report Signing') {
|
||||
throw new Error('Leaf cert CN field had unexpected value');
|
||||
}
|
||||
const fieldO = leafCert.subject.getField('O');
|
||||
if (!fieldO || fieldO.value !== 'Intel Corporation') {
|
||||
throw new Error('Leaf cert O field had unexpected value');
|
||||
}
|
||||
const fieldL = leafCert.subject.getField('L');
|
||||
if (!fieldL || fieldL.value !== 'Santa Clara') {
|
||||
throw new Error('Leaf cert L field had unexpected value');
|
||||
}
|
||||
const fieldST = leafCert.subject.getField('ST');
|
||||
if (!fieldST || fieldST.value !== 'CA') {
|
||||
throw new Error('Leaf cert ST field had unexpected value');
|
||||
}
|
||||
const fieldC = leafCert.subject.getField('C');
|
||||
if (!fieldC || fieldC.value !== 'US') {
|
||||
throw new Error('Leaf cert C field had unexpected value');
|
||||
}
|
||||
}
|
||||
|
||||
private validateAttestationQuote({
|
||||
serverStaticPublic,
|
||||
quote: quoteBytes,
|
||||
}: {
|
||||
serverStaticPublic: Uint8Array;
|
||||
quote: Uint8Array;
|
||||
}): void {
|
||||
const SGX_CONSTANTS = getSgxConstants();
|
||||
const quote = Buffer.from(quoteBytes);
|
||||
|
||||
const quoteVersion = quote.readInt16LE(0) & 0xffff;
|
||||
if (quoteVersion < 0 || quoteVersion > 2) {
|
||||
throw new Error(`Unknown version ${quoteVersion}`);
|
||||
}
|
||||
|
||||
const miscSelect = quote.slice(64, 64 + 4);
|
||||
if (!miscSelect.every(byte => byte === 0)) {
|
||||
throw new Error('Quote miscSelect invalid!');
|
||||
}
|
||||
|
||||
const reserved1 = quote.slice(68, 68 + 28);
|
||||
if (!reserved1.every(byte => byte === 0)) {
|
||||
throw new Error('Quote reserved1 invalid!');
|
||||
}
|
||||
|
||||
const flags = Long.fromBytesLE(
|
||||
Array.from(quote.slice(96, 96 + 8).values())
|
||||
);
|
||||
if (
|
||||
flags.and(SGX_CONSTANTS.SGX_FLAGS_RESERVED).notEquals(0) ||
|
||||
flags.and(SGX_CONSTANTS.SGX_FLAGS_INITTED).equals(0) ||
|
||||
flags.and(SGX_CONSTANTS.SGX_FLAGS_MODE64BIT).equals(0)
|
||||
) {
|
||||
throw new Error(`Quote flags invalid ${flags.toString()}`);
|
||||
}
|
||||
|
||||
const xfrm = Long.fromBytesLE(
|
||||
Array.from(quote.slice(104, 104 + 8).values())
|
||||
);
|
||||
if (xfrm.and(SGX_CONSTANTS.SGX_XFRM_RESERVED).notEquals(0)) {
|
||||
throw new Error(`Quote xfrm invalid ${xfrm}`);
|
||||
}
|
||||
|
||||
const mrenclave = quote.slice(112, 112 + 32);
|
||||
const enclaveIdBytes = Bytes.fromHex(this.options.directoryEnclaveId);
|
||||
if (mrenclave.compare(enclaveIdBytes) !== 0) {
|
||||
throw new Error('Quote mrenclave invalid!');
|
||||
}
|
||||
|
||||
const reserved2 = quote.slice(144, 144 + 32);
|
||||
if (!reserved2.every(byte => byte === 0)) {
|
||||
throw new Error('Quote reserved2 invalid!');
|
||||
}
|
||||
|
||||
const reportData = quote.slice(368, 368 + 64);
|
||||
const serverStaticPublicBytes = serverStaticPublic;
|
||||
if (
|
||||
!reportData.every((byte, index) => {
|
||||
if (index >= 32) {
|
||||
return byte === 0;
|
||||
}
|
||||
return byte === serverStaticPublicBytes[index];
|
||||
})
|
||||
) {
|
||||
throw new Error('Quote report_data invalid!');
|
||||
}
|
||||
|
||||
const reserved3 = quote.slice(208, 208 + 96);
|
||||
if (!reserved3.every(byte => byte === 0)) {
|
||||
throw new Error('Quote reserved3 invalid!');
|
||||
}
|
||||
|
||||
const reserved4 = quote.slice(308, 308 + 60);
|
||||
if (!reserved4.every(byte => byte === 0)) {
|
||||
throw new Error('Quote reserved4 invalid!');
|
||||
}
|
||||
|
||||
const signatureLength = quote.readInt32LE(432) >>> 0;
|
||||
if (signatureLength !== quote.byteLength - 436) {
|
||||
throw new Error(`Bad signatureLength ${signatureLength}`);
|
||||
}
|
||||
|
||||
// const signature = quote.slice(436, 436 + signatureLength);
|
||||
}
|
||||
}
|
||||
|
||||
const ALLOWED_ADVISORIES = new Set(['INTEL-SA-00334', 'INTEL-SA-00615']);
|
||||
|
||||
function validateAttestationSignatureBody(
|
||||
signatureBody: {
|
||||
timestamp: string;
|
||||
version: number;
|
||||
isvEnclaveQuoteBody: string;
|
||||
isvEnclaveQuoteStatus: string;
|
||||
advisoryIDs: ReadonlyArray<string>;
|
||||
},
|
||||
encodedQuote: string
|
||||
) {
|
||||
// Parse timestamp as UTC
|
||||
const { timestamp } = signatureBody;
|
||||
const utcTimestamp = timestamp.endsWith('Z') ? timestamp : `${timestamp}Z`;
|
||||
const signatureTime = new Date(utcTimestamp).getTime();
|
||||
|
||||
const now = Date.now();
|
||||
if (signatureBody.version !== 4) {
|
||||
throw new Error('Attestation signature invalid version!');
|
||||
}
|
||||
if (!encodedQuote.startsWith(signatureBody.isvEnclaveQuoteBody)) {
|
||||
throw new Error('Attestion signature mismatches quote!');
|
||||
}
|
||||
if (signatureBody.isvEnclaveQuoteStatus !== 'SW_HARDENING_NEEDED') {
|
||||
throw new Error('Attestation signature status not "SW_HARDENING_NEEDED"!');
|
||||
}
|
||||
if (!signatureBody.advisoryIDs.every(id => ALLOWED_ADVISORIES.has(id))) {
|
||||
throw new Error('Attestation advisory ids are incorrect');
|
||||
}
|
||||
if (signatureBody.advisoryIDs.length > ALLOWED_ADVISORIES.size) {
|
||||
throw new Error('Attestation advisory count is incorrect');
|
||||
}
|
||||
if (signatureTime < now - 24 * 60 * 60 * 1000) {
|
||||
throw new Error('Attestation signature timestamp older than 24 hours!');
|
||||
}
|
||||
}
|
|
@ -11,53 +11,16 @@ export type ConfigRequiredStringType = z.infer<
|
|||
typeof configRequiredStringSchema
|
||||
>;
|
||||
|
||||
const configOptionalUnknownSchema = configRequiredStringSchema.or(z.unknown());
|
||||
|
||||
const configOptionalStringSchema = configRequiredStringSchema.or(z.undefined());
|
||||
export type configOptionalStringType = z.infer<
|
||||
typeof configOptionalStringSchema
|
||||
>;
|
||||
|
||||
const directoryLegacyConfigSchema = z.object({
|
||||
directoryType: z.literal('legacy'),
|
||||
directoryEnclaveId: configRequiredStringSchema,
|
||||
directoryTrustAnchor: configRequiredStringSchema,
|
||||
export const directoryConfigSchema = z.object({
|
||||
directoryUrl: configRequiredStringSchema,
|
||||
directoryMRENCLAVE: configRequiredStringSchema,
|
||||
});
|
||||
|
||||
const directoryCDSIConfigSchema = z.object({
|
||||
directoryType: z.literal('cdsi'),
|
||||
directoryCDSIUrl: configRequiredStringSchema,
|
||||
directoryCDSIMRENCLAVE: configRequiredStringSchema,
|
||||
});
|
||||
|
||||
const directoryMirroredCDSIConfigSchema = z.object({
|
||||
directoryType: z.literal('mirrored-cdsi'),
|
||||
|
||||
directoryEnclaveId: configRequiredStringSchema,
|
||||
directoryTrustAnchor: configRequiredStringSchema,
|
||||
directoryUrl: configRequiredStringSchema,
|
||||
|
||||
directoryCDSIUrl: configRequiredStringSchema,
|
||||
directoryCDSIMRENCLAVE: configRequiredStringSchema,
|
||||
});
|
||||
|
||||
export const directoryConfigSchema = z
|
||||
.object({
|
||||
// Unknown defaults
|
||||
directoryEnclaveId: configOptionalUnknownSchema,
|
||||
directoryTrustAnchor: configOptionalUnknownSchema,
|
||||
directoryUrl: configOptionalUnknownSchema,
|
||||
|
||||
directoryCDSIUrl: configOptionalUnknownSchema,
|
||||
directoryCDSIMRENCLAVE: configOptionalUnknownSchema,
|
||||
})
|
||||
.and(
|
||||
directoryLegacyConfigSchema
|
||||
.or(directoryMirroredCDSIConfigSchema)
|
||||
.or(directoryCDSIConfigSchema)
|
||||
);
|
||||
|
||||
export type DirectoryConfigType = z.infer<typeof directoryConfigSchema>;
|
||||
|
||||
export const rendererConfigSchema = z.object({
|
||||
|
|
|
@ -7,7 +7,6 @@ import type { UUIDStringType } from '../types/UUID';
|
|||
import * as log from '../logging/log';
|
||||
import { isEnabled } from '../RemoteConfig';
|
||||
import { isDirectConversation, isMe } from './whatTypeOfConversation';
|
||||
import { isBeta } from './version';
|
||||
|
||||
export async function getUuidsForE164s(
|
||||
server: Pick<WebAPIType, 'cdsLookup'>,
|
||||
|
@ -40,11 +39,6 @@ export async function getUuidsForE164s(
|
|||
}
|
||||
|
||||
const returnAcisWithoutUaks = isEnabled('desktop.cdsi.returnAcisWithoutUaks');
|
||||
const isCDSI =
|
||||
isEnabled('desktop.cdsi') ||
|
||||
(isBeta(window.getVersion()) && isEnabled('desktop.cdsi.beta'));
|
||||
|
||||
const isMirroring = isEnabled('desktop.cdsi.mirroring');
|
||||
|
||||
log.info(
|
||||
`getUuidsForE164s(${e164s}): acis=${acis.length} ` +
|
||||
|
@ -55,7 +49,5 @@ export async function getUuidsForE164s(
|
|||
acis,
|
||||
accessKeys,
|
||||
returnAcisWithoutUaks,
|
||||
isLegacy: !isCDSI,
|
||||
isMirroring,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -3498,13 +3498,6 @@
|
|||
"@types/node" "*"
|
||||
form-data "^3.0.0"
|
||||
|
||||
"@types/node-forge@0.9.5":
|
||||
version "0.9.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/node-forge/-/node-forge-0.9.5.tgz#648231d79da197216290429020698d4e767365a0"
|
||||
integrity sha512-rrN3xfA/oZIzwOnO3d2wRQz7UdeVkmMMPjWUCfpPTPuKFVb3D6G10LuiVHYYmvrivBBLMx4m0P/FICoDbNZUMA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/node@*", "@types/node@>=13.7.0":
|
||||
version "17.0.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.17.tgz#a8ddf6e0c2341718d74ee3dc413a13a042c45a0c"
|
||||
|
@ -12741,7 +12734,7 @@ node-fetch@2.6.7, node-fetch@^2.6.0, node-fetch@^2.6.1, node-fetch@^2.6.7:
|
|||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
node-forge@1.3.0, node-forge@^1.2.0:
|
||||
node-forge@^1.2.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.0.tgz#37a874ea723855f37db091e6c186e5b67a01d4b2"
|
||||
integrity sha512-08ARB91bUi6zNKzVmaj3QO7cr397uiDT2nJ63cHjyNtCTWIgvS47j3eT0WfzUwS9+6Z5YshRaoasFkXCKrIYbA==
|
||||
|
|
Loading…
Add table
Reference in a new issue