mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-04-26 17:03:43 +02:00
(1) switch NIST curve to P-384 since there is no point in 521 and NSA may not even accept it, (2) simplify ephemeral key stuff, (3) toss some stuff in the attic.
This commit is contained in:
parent
06c0d452f4
commit
4a6e88344c
31 changed files with 1803 additions and 1230 deletions
|
@ -10,11 +10,14 @@ lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
panic = 'abort'
|
panic = 'abort'
|
||||||
|
|
||||||
[target."cfg(all(not(any(target_os = \"macos\", target_os = \"ios\")), any(target_arch = \"s390x\", target_arch = \"powerpc64le\", target_arch = \"powerpc64\")))".dependencies]
|
[target."cfg(not(any(target_os = \"macos\", target_os = \"ios\")))".dependencies]
|
||||||
openssl = "^0"
|
openssl = "^0"
|
||||||
|
|
||||||
[target."cfg(not(any(target_os = \"macos\", target_os = \"ios\", target_arch = \"s390x\", target_arch = \"powerpc64le\", target_arch = \"powerpc64\")))".dependencies]
|
#[target."cfg(all(not(any(target_os = \"macos\", target_os = \"ios\")), any(target_arch = \"s390x\", target_arch = \"powerpc64le\", target_arch = \"powerpc64\")))".dependencies]
|
||||||
gcrypt = "^0"
|
#openssl = "^0"
|
||||||
|
|
||||||
|
#[target."cfg(not(any(target_os = \"macos\", target_os = \"ios\", target_arch = \"s390x\", target_arch = \"powerpc64le\", target_arch = \"powerpc64\")))".dependencies]
|
||||||
|
#gcrypt = "^0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
sha2 = "^0"
|
sha2 = "^0"
|
||||||
|
|
|
@ -9,19 +9,21 @@
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
mod impl_macos;
|
mod impl_macos;
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "ios", target_arch = "s390x", target_arch = "powerpc64le", target_arch = "powerpc64")))]
|
//#[cfg(not(any(target_os = "macos", target_os = "ios", target_arch = "s390x", target_arch = "powerpc64le", target_arch = "powerpc64")))]
|
||||||
mod impl_gcrypt;
|
//mod impl_gcrypt;
|
||||||
|
|
||||||
#[cfg(all(not(any(target_os = "macos", target_os = "ios")), any(target_arch = "s390x", target_arch = "powerpc64le", target_arch = "powerpc64")))]
|
//#[cfg(all(not(any(target_os = "macos", target_os = "ios")), any(target_arch = "s390x", target_arch = "powerpc64le", target_arch = "powerpc64")))]
|
||||||
|
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||||
mod impl_openssl;
|
mod impl_openssl;
|
||||||
|
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
pub use impl_macos::{AesCtr, AesGmacSiv};
|
pub use impl_macos::{AesCtr, AesGmacSiv};
|
||||||
|
|
||||||
#[cfg(not(any(target_os = "macos", target_os = "ios", target_arch = "s390x", target_arch = "powerpc64le", target_arch = "powerpc64")))]
|
//#[cfg(not(any(target_os = "macos", target_os = "ios", target_arch = "s390x", target_arch = "powerpc64le", target_arch = "powerpc64")))]
|
||||||
pub use impl_gcrypt::{AesCtr, AesGmacSiv};
|
//pub use impl_gcrypt::{AesCtr, AesGmacSiv};
|
||||||
|
|
||||||
#[cfg(all(not(any(target_os = "macos", target_os = "ios")), any(target_arch = "s390x", target_arch = "powerpc64le", target_arch = "powerpc64")))]
|
//#[cfg(all(not(any(target_os = "macos", target_os = "ios")), any(target_arch = "s390x", target_arch = "powerpc64le", target_arch = "powerpc64")))]
|
||||||
|
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||||
pub use impl_openssl::{AesCtr, AesGmacSiv};
|
pub use impl_openssl::{AesCtr, AesGmacSiv};
|
||||||
|
|
||||||
pub(crate) const ZEROES: [u8; 16] = [0_u8; 16];
|
pub(crate) const ZEROES: [u8; 16] = [0_u8; 16];
|
||||||
|
|
65
attic/zerotier-forward-secrecy.md
Normal file
65
attic/zerotier-forward-secrecy.md
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
# ZeroTier Forward Secrecy Design (draft)
|
||||||
|
|
||||||
|
Author: Adam Ierymenko / adam.ierymenko@zerotier.com
|
||||||
|
|
||||||
|
## Design Goals
|
||||||
|
|
||||||
|
- Implement security qualities of more modern protocols like Wireguard or Signal.
|
||||||
|
- Support FIPS compliance because we have a bunch of customers who want it.
|
||||||
|
- Continue to use a non-FIPS algorithm too because another huge camp of users are afraid of NIST ECC curves.
|
||||||
|
- Improve hardening against DOS and replay attacks.
|
||||||
|
|
||||||
|
## Algorithms
|
||||||
|
|
||||||
|
- AES-GMAC-SIV for authenticated symmetric encryption (not described here).
|
||||||
|
- HMAC-SHA512 for key derivation.
|
||||||
|
- Curve25519 for asymmetric key agreement
|
||||||
|
- NIST P-521 for asymmetric key agreement
|
||||||
|
- *Maybe* a PQ candidate algorithm to protect against scenarios in which data is warehoused until a QC is available. Considering SIDH, CRYSTALS-KYBER, or NTRU.
|
||||||
|
- Can't easily use the "throw in a static secret" trick used by WireGuard since ZT VL1 sessions are shared among multiple trust boundaries if one is a member of multiple overlapping virtual networks. Which static secret would one use in that case? What if network membership changes? We don't want to set up N totally redundant VL1 sessions between Alice and Bob if they share membership in N networks.
|
||||||
|
|
||||||
|
## Hybrid Cryptography
|
||||||
|
|
||||||
|
Supporting both FIPS and non-FIPS and possibly a PQ algorithm requires the use of hybrid cryptography. This is achieved by performing each KEX in the re-key sequence with multiple algorithms and combining the results. Combination is via HMAC-SHA384(previous, current) where previous is the "key" and current is the "message."
|
||||||
|
|
||||||
|
Exchange uses all algorithms mutually supported by both sides (bitwise AND). This seems less vulnerable to a potential future downgrade attack and to strengthen key agreement overall. All algorithms would have to be broken to break the result of such a chain, making it always as strong as the strongest algorithm.
|
||||||
|
|
||||||
|
FIPS compliance can be achieved by always placing FIPS-compliant algorithms at the end of the list of chained algorithms. So if we are using curve25519 and NIST P-521 the final master key is HMAC-SHA384(curve25519 secret, NIST P-521 secret). FIPS would consider the curve25519 secret a "salt," and FIPS documents do not specify where the salt must originate or if it must be private or public. It doesn't matter cryptographically since the output of HMAC-SHA384(public, secret) is secret.
|
||||||
|
|
||||||
|
*At some point I must write a blog post on smuggling better crypto into FIPS environments through clever use of FIPS primitives.*
|
||||||
|
|
||||||
|
## Session Example
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
|
||||||
|
- Alice starts knowing Bob's identity (which contains long-lived identity key(s)), and vice versa. These are obtained via root nodes which act as identity caches.
|
||||||
|
- Consider public and private keys to actually be bundles of keys containing a key for each algorithm.
|
||||||
|
|
||||||
|
1. Each side sends HELLO containing:
|
||||||
|
- 64-bit counter / nonce
|
||||||
|
- ZeroTier address information (authenticated but not encrypted)
|
||||||
|
- Message type
|
||||||
|
- Sender's ZeroTier identity with their long-lived public key(s)
|
||||||
|
- Encrypted payload section:
|
||||||
|
- Ephemeral public key(s) of initiating node
|
||||||
|
- Wall clock timestamp (milliseconds since epoch)
|
||||||
|
- Monotonic timestamp (milliseconds since some time in the past)
|
||||||
|
- HMAC-SHA512(previous session key) (using static identity key)
|
||||||
|
- Other ZeroTier-related fields (not cryptographically important)
|
||||||
|
- SHA512 hash of recipient's ZeroTier identity
|
||||||
|
- Full HMAC-SHA512 of entire HELLO packet (using static identity key)
|
||||||
|
|
||||||
|
2. Recipients of HELLO respond with OK(HELLO) containing:
|
||||||
|
- Standard ZeroTier encrypted packet headers, addresses, etc.
|
||||||
|
- Ephemeral public key(s) of responding node
|
||||||
|
- Wall clock timestamp (milliseconds since epoch)
|
||||||
|
- Monotonic timestamp (milliseconds since some time in the past)
|
||||||
|
- HMAC-SHA512(new session key) (using static identity key)
|
||||||
|
- Other ZeroTier-related fields (not cryptographically important)
|
||||||
|
- Full HMAC-SHA512 of entire OK(HELLO) packet (using static identity key)
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
- https://soatok.blog/2022/01/27/the-controversy-surrounding-hybrid-cryptography/
|
||||||
|
- https://www.wireguard.com/papers/wireguard.pdf
|
||||||
|
- https://www.signal.org/blog/advanced-ratcheting/
|
702
egads/Cargo.lock
generated
Normal file
702
egads/Cargo.lock
generated
Normal file
|
@ -0,0 +1,702 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes-gmac-siv"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"openssl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "0.7.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "atomic-polyfill"
|
||||||
|
version = "0.1.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ee6adc1648f03fbc1bc1b5cf0f2fdfb5edbc96215b711edcfe6ce2641ef9b347"
|
||||||
|
dependencies = [
|
||||||
|
"critical-section",
|
||||||
|
"riscv-target",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "autocfg"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bare-metal"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3"
|
||||||
|
dependencies = [
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bare-metal"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit_field"
|
||||||
|
version = "0.10.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitfield"
|
||||||
|
version = "0.13.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cc"
|
||||||
|
version = "1.0.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cfg-if"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cortex-m"
|
||||||
|
version = "0.7.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "37ff967e867ca14eba0c34ac25cd71ea98c678e741e3915d923999bb2fe7c826"
|
||||||
|
dependencies = [
|
||||||
|
"bare-metal 0.2.5",
|
||||||
|
"bitfield",
|
||||||
|
"embedded-hal",
|
||||||
|
"volatile-register",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cpufeatures"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95059428f66df56b63431fdb4e1947ed2190586af5c5a8a8b71122bdf5a7f469"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "critical-section"
|
||||||
|
version = "0.2.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70"
|
||||||
|
dependencies = [
|
||||||
|
"bare-metal 1.0.0",
|
||||||
|
"cfg-if",
|
||||||
|
"cortex-m",
|
||||||
|
"riscv",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek"
|
||||||
|
version = "3.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"digest",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74e1069e39f1454367eb2de793ed062fac4c35c2934b76a81d90dd9abcd28816"
|
||||||
|
dependencies = [
|
||||||
|
"signature",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519-dalek"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c762bae6dcaf24c4c84667b8579785430908723d5c889f469d76a41d59cc7a9d"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek",
|
||||||
|
"ed25519",
|
||||||
|
"rand",
|
||||||
|
"serde",
|
||||||
|
"sha2",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egads"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"rmp-serde",
|
||||||
|
"serde",
|
||||||
|
"zerotier-core-crypto",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "embedded-hal"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff"
|
||||||
|
dependencies = [
|
||||||
|
"nb 0.1.3",
|
||||||
|
"void",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
|
||||||
|
dependencies = [
|
||||||
|
"foreign-types-shared",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "foreign-types-shared"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.7.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d076121838e03f862871315477528debffdb7462fb229216ecef91b1a3eb31eb"
|
||||||
|
dependencies = [
|
||||||
|
"atomic-polyfill",
|
||||||
|
"hash32",
|
||||||
|
"spin",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy_static"
|
||||||
|
version = "1.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.118"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06e509672465a0504304aa87f9f176f2b2b716ed8fb105ebe5c02dc6dce96a94"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lock_api"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f"
|
||||||
|
dependencies = [
|
||||||
|
"nb 1.0.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nb"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-traits"
|
||||||
|
version = "0.2.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "da32515d9f6e6e489d7bc9d84c71b060db7247dc035bbe44eac88cf87486d8d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "opaque-debug"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl"
|
||||||
|
version = "0.10.38"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c7ae222234c30df141154f159066c5093ff73b63204dcda7121eb082fc56a95"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"cfg-if",
|
||||||
|
"foreign-types",
|
||||||
|
"libc",
|
||||||
|
"once_cell",
|
||||||
|
"openssl-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "openssl-sys"
|
||||||
|
version = "0.9.72"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7e46109c383602735fa0a2e48dd2b7c892b048e1bf69e5c3b1d804b7d9c203cb"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"cc",
|
||||||
|
"libc",
|
||||||
|
"pkg-config",
|
||||||
|
"vcpkg",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkg-config"
|
||||||
|
version = "0.3.24"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "poly1305"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede"
|
||||||
|
dependencies = [
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
"universal-hash",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ppv-lite86"
|
||||||
|
version = "0.2.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.36"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand"
|
||||||
|
version = "0.7.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
"libc",
|
||||||
|
"rand_chacha",
|
||||||
|
"rand_core",
|
||||||
|
"rand_hc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_chacha"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
|
||||||
|
dependencies = [
|
||||||
|
"ppv-lite86",
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_hc"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
|
||||||
|
dependencies = [
|
||||||
|
"rand_core",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex"
|
||||||
|
version = "1.5.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.6.25"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "riscv"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba"
|
||||||
|
dependencies = [
|
||||||
|
"bare-metal 1.0.0",
|
||||||
|
"bit_field",
|
||||||
|
"riscv-target",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "riscv-target"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222"
|
||||||
|
dependencies = [
|
||||||
|
"lazy_static",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp"
|
||||||
|
version = "0.8.10"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4f55e5fa1446c4d5dd1f5daeed2a4fe193071771a2636274d0d7a3b082aa7ad6"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"num-traits",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rmp-serde"
|
||||||
|
version = "0.15.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "723ecff9ad04f4ad92fe1c8ca6c20d2196d9286e9c60727c4cb5511629260e9d"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"rmp",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scopeguard"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.136"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.136"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.9.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer",
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest",
|
||||||
|
"opaque-debug",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signature"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f054c6c1a6e95179d6f23ed974060dcefb2d9388bb7256900badad682c499de4"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spin"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
|
||||||
|
dependencies = [
|
||||||
|
"lock_api",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "subtle"
|
||||||
|
version = "2.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "1.0.86"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.12.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "typenum"
|
||||||
|
version = "1.15.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-xid"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "universal-hash"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcell"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "vcpkg"
|
||||||
|
version = "0.2.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "void"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "volatile-register"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6"
|
||||||
|
dependencies = [
|
||||||
|
"vcell",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.9.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x25519-dalek"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2392b6b94a576b4e2bf3c5b2757d63f10ada8020a2e4d08ac849ebcf6ea8e077"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek",
|
||||||
|
"rand_core",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "81e8f13fef10b63c06356d65d416b070798ddabcadc10d3ece0c5be9b3c7eddb"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zerotier-core-crypto"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aes-gmac-siv",
|
||||||
|
"ed25519-dalek",
|
||||||
|
"foreign-types",
|
||||||
|
"heapless",
|
||||||
|
"lazy_static",
|
||||||
|
"openssl",
|
||||||
|
"poly1305",
|
||||||
|
"rand_core",
|
||||||
|
"subtle",
|
||||||
|
"x25519-dalek",
|
||||||
|
]
|
9
egads/Cargo.toml
Normal file
9
egads/Cargo.toml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "egads"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
zerotier-core-crypto = { path = "../zerotier-core-crypto" }
|
||||||
|
serde = { version = "^1", features = ["derive"], default-features = false }
|
||||||
|
rmp-serde = "^0"
|
3
egads/README.md
Normal file
3
egads/README.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
EGADS: Embeddable Globally Authenticated Data Store
|
||||||
|
=====
|
||||||
|
|
8
egads/src/block.rs
Normal file
8
egads/src/block.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
pub struct Block<'a> {
|
||||||
|
pub validator: &'a [u8],
|
||||||
|
pub parent: &'a [u8],
|
||||||
|
pub timestamp: u64,
|
||||||
|
pub height: u64,
|
||||||
|
pub records: &'a [u8],
|
||||||
|
pub signature: &'a [u8],
|
||||||
|
}
|
1
egads/src/lib.rs
Normal file
1
egads/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
||||||
|
mod block;
|
|
@ -14,11 +14,14 @@ panic = 'abort'
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rand_core = "0.5.0"
|
rand_core = "0.5.0"
|
||||||
aes-gmac-siv = { path = "../aes-gmac-siv" }
|
aes-gmac-siv = { path = "../aes-gmac-siv" }
|
||||||
gcrypt = "0.7.0"
|
|
||||||
x25519-dalek = { version = "1.2.0", features = ["u64_backend"] }
|
x25519-dalek = { version = "1.2.0", features = ["u64_backend"] }
|
||||||
ed25519-dalek = { version = "1.0.1", features = ["u64_backend"] }
|
ed25519-dalek = { version = "1.0.1", features = ["u64_backend"] }
|
||||||
heapless = "0.7.7"
|
heapless = "0.7.7"
|
||||||
subtle = "2.4.1"
|
subtle = "2.4.1"
|
||||||
|
openssl = { version = "^0", features = [], default-features = false }
|
||||||
|
lazy_static = "^1"
|
||||||
|
foreign-types = "0.3.1"
|
||||||
|
poly1305 = { version = "0.7.2", features = [], default-features = false }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = "1.0.3"
|
quickcheck = "1.0.3"
|
||||||
|
|
|
@ -1,143 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*
|
|
||||||
* (c)2021 ZeroTier, Inc.
|
|
||||||
* https://www.zerotier.com/
|
|
||||||
*/
|
|
||||||
|
|
||||||
use std::mem::MaybeUninit;
|
|
||||||
|
|
||||||
use crate::hash::{SHA384, SHA512};
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn hash_int_le(sha: &mut SHA512, i: u64) {
|
|
||||||
#[cfg(target_endian = "big")] {
|
|
||||||
sha.update(&i.to_le_bytes());
|
|
||||||
}
|
|
||||||
#[cfg(target_endian = "little")] {
|
|
||||||
sha.update(unsafe { &*(&i as *const u64).cast::<[u8; 8]>() });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Compute balloon (variant) memory-hard hash.
|
|
||||||
///
|
|
||||||
/// SPACE_COST must be a multiple of 64. This is checked with an assertion.
|
|
||||||
/// DELTA is usually 3.
|
|
||||||
///
|
|
||||||
/// This differs slightly from "standard" balloon hash in that AES (CBC) is
|
|
||||||
/// used for the expand step and the final hash hashes the entire buffer. It
|
|
||||||
/// also takes no salt since it's only used for one purpose here and that's
|
|
||||||
/// not password hashing.
|
|
||||||
pub fn zt_variant_hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>(password: &[u8]) -> [u8; crate::hash::SHA384_HASH_SIZE] {
|
|
||||||
debug_assert_ne!(SPACE_COST, 0);
|
|
||||||
debug_assert_ne!(TIME_COST, 0);
|
|
||||||
debug_assert_ne!(DELTA, 0);
|
|
||||||
debug_assert_eq!((SPACE_COST % 64), 0);
|
|
||||||
|
|
||||||
let mut buf: [u8; SPACE_COST] = unsafe { MaybeUninit::uninit().assume_init() };
|
|
||||||
let zero64 = [0_u8; 8];
|
|
||||||
|
|
||||||
/* Initial hash */
|
|
||||||
let mut sha = SHA512::new();
|
|
||||||
sha.update(&zero64); // 0 cnt
|
|
||||||
sha.update(password);
|
|
||||||
buf[0..64].copy_from_slice(sha.finish_get_ref());
|
|
||||||
|
|
||||||
/* Expand (use AES as PRNG in this version as it's much faster on most hardware) */
|
|
||||||
let mut expand_aes = gcrypt::cipher::Cipher::new(gcrypt::cipher::Algorithm::Aes, gcrypt::cipher::Mode::Ecb).unwrap();
|
|
||||||
let _ = expand_aes.set_key(&buf[0..32]);
|
|
||||||
let mut s: usize = 64;
|
|
||||||
while s < SPACE_COST {
|
|
||||||
let ss = s + 16;
|
|
||||||
let _ = expand_aes.encrypt(unsafe { &*buf.as_ptr().add(s - 16).cast::<[u8; 16]>() }, &mut buf[s..ss]);
|
|
||||||
s = ss;
|
|
||||||
}
|
|
||||||
drop(expand_aes);
|
|
||||||
|
|
||||||
/* Mix */
|
|
||||||
let mut cnt = 1_u64 + ((SPACE_COST / 16) as u64);
|
|
||||||
for t in 0..TIME_COST {
|
|
||||||
sha.reset();
|
|
||||||
hash_int_le(&mut sha, cnt);
|
|
||||||
sha.update(&buf[(SPACE_COST - 64)..SPACE_COST]); // "previous" initially wraps back around to end
|
|
||||||
sha.update(&buf[0..64]);
|
|
||||||
buf[0..64].copy_from_slice(sha.finish_get_ref());
|
|
||||||
cnt += 1;
|
|
||||||
|
|
||||||
for i in 0..DELTA {
|
|
||||||
sha.reset();
|
|
||||||
hash_int_le(&mut sha, cnt);
|
|
||||||
hash_int_le(&mut sha, t as u64);
|
|
||||||
sha.update(&zero64); // s == 0
|
|
||||||
hash_int_le(&mut sha, i as u64);
|
|
||||||
cnt += 1;
|
|
||||||
|
|
||||||
let other = sha.finish_get_ref();
|
|
||||||
let other = ((u64::from_le_bytes(unsafe { *other.as_ptr().cast::<[u8; 8]>() }) % (SPACE_COST as u64 / 64)) * 64) as usize;
|
|
||||||
|
|
||||||
sha.reset();
|
|
||||||
hash_int_le(&mut sha, cnt);
|
|
||||||
sha.update(&buf[0..64]);
|
|
||||||
sha.update(&buf[other..(other + 64)]);
|
|
||||||
buf[0..64].copy_from_slice(sha.finish_get_ref());
|
|
||||||
cnt += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut s = 64;
|
|
||||||
while s < SPACE_COST {
|
|
||||||
sha.reset();
|
|
||||||
hash_int_le(&mut sha, cnt);
|
|
||||||
sha.update(&buf[(s - 64)..s]);
|
|
||||||
let ss = s + 64;
|
|
||||||
sha.update(&buf[s..ss]);
|
|
||||||
buf[s..ss].copy_from_slice(sha.finish_get_ref());
|
|
||||||
cnt += 1;
|
|
||||||
|
|
||||||
for i in 0..DELTA {
|
|
||||||
sha.reset();
|
|
||||||
hash_int_le(&mut sha, cnt);
|
|
||||||
hash_int_le(&mut sha, t as u64);
|
|
||||||
hash_int_le(&mut sha, s as u64);
|
|
||||||
hash_int_le(&mut sha, i as u64);
|
|
||||||
cnt += 1;
|
|
||||||
|
|
||||||
let other = sha.finish_get_ref();
|
|
||||||
let other = ((u64::from_le_bytes(unsafe { *other.as_ptr().cast::<[u8; 8]>() }) % (SPACE_COST as u64 / 64)) * 64) as usize;
|
|
||||||
|
|
||||||
sha.reset();
|
|
||||||
hash_int_le(&mut sha, cnt);
|
|
||||||
sha.update(&buf[s..ss]);
|
|
||||||
sha.update(&buf[other..(other + 64)]);
|
|
||||||
buf[s..ss].copy_from_slice(sha.finish_get_ref());
|
|
||||||
cnt += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
s = ss;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Standard balloon hashing just returns the last hash in the shuffled array.
|
|
||||||
// We use AES to init the array to make things more memory bound and less CPU
|
|
||||||
// bound and we want a 384-bit result, so use SHA384 over the whole array
|
|
||||||
// instead. We also use the FIPS/NIST HMAC(salt, key) construction in case
|
|
||||||
// someone complains about FIPS stuff even though this is not a KDF.
|
|
||||||
SHA384::hmac(&buf, password)
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
#[test]
|
|
||||||
fn balloon_test() {
|
|
||||||
let start = std::time::SystemTime::now();
|
|
||||||
let mut tmp = 0_u8;
|
|
||||||
for _ in 0..100 {
|
|
||||||
let foo = crate::balloon::hash::<16384, 3, 3>(&[1_u8], &[2_u8]);
|
|
||||||
tmp = tmp.wrapping_add(foo[0]);
|
|
||||||
}
|
|
||||||
let duration = std::time::SystemTime::now().duration_since(start).unwrap();
|
|
||||||
println!("Benchmark: {}ms per hash (junk to prevent optimizing out: {})", (duration.as_nanos() as f64 / 100.0) / 1000000.0, tmp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -26,7 +26,7 @@ pub struct C25519KeyPair(x25519_dalek::StaticSecret, x25519_dalek::PublicKey);
|
||||||
|
|
||||||
impl C25519KeyPair {
|
impl C25519KeyPair {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn generate(_transient: bool) -> C25519KeyPair {
|
pub fn generate() -> C25519KeyPair {
|
||||||
let sk = x25519_dalek::StaticSecret::new(SecureRandom::get());
|
let sk = x25519_dalek::StaticSecret::new(SecureRandom::get());
|
||||||
let pk = x25519_dalek::PublicKey::from(&sk);
|
let pk = x25519_dalek::PublicKey::from(&sk);
|
||||||
C25519KeyPair(sk, pk)
|
C25519KeyPair(sk, pk)
|
||||||
|
@ -68,7 +68,7 @@ pub struct Ed25519KeyPair(ed25519_dalek::Keypair);
|
||||||
|
|
||||||
impl Ed25519KeyPair {
|
impl Ed25519KeyPair {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn generate(_transient: bool) -> Ed25519KeyPair {
|
pub fn generate() -> Ed25519KeyPair {
|
||||||
let mut rng = SecureRandom::get();
|
let mut rng = SecureRandom::get();
|
||||||
Ed25519KeyPair(ed25519_dalek::Keypair::generate(&mut rng))
|
Ed25519KeyPair(ed25519_dalek::Keypair::generate(&mut rng))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
use std::io::Write;
|
|
||||||
|
|
||||||
/// GMAC portion of AES-GCM for use as a fast plain vanilla MAC.
|
|
||||||
pub struct GMAC(gcrypt::mac::Mac);
|
|
||||||
|
|
||||||
impl GMAC {
|
|
||||||
/// Create a new keyed GMAC instance.
|
|
||||||
/// The key may be 16, 24, or 32 bytes in length. This will panic otherwise.
|
|
||||||
pub fn new(key: &[u8]) -> GMAC {
|
|
||||||
if key.len() != 32 && key.len() != 24 && key.len() != 16 {
|
|
||||||
panic!("AES supports 128, 192, or 256 bits keys");
|
|
||||||
}
|
|
||||||
let mut m = GMAC(gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::GmacAes).unwrap());
|
|
||||||
m.0.set_key(key).expect("GMAC set_key failed");
|
|
||||||
m
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset GMAC and set nonce.
|
|
||||||
/// The nonce may be anywhere from 8 to 16 bytes in length but 12 bytes is strongly recommended.
|
|
||||||
/// It may be sequential.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn init(&mut self, nonce: &[u8]) {
|
|
||||||
let _ = self.0.reset();
|
|
||||||
self.0.set_iv(nonce).expect("GMAC set_iv failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn update(&mut self, data: &[u8]) {
|
|
||||||
let _ = self.0.update(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Flush GMAC and filll 'mac' with the final authentication code.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn finish(&mut self, mac: &mut [u8; 16]) {
|
|
||||||
let _ = self.0.flush();
|
|
||||||
let _ = self.0.get_mac(mac).expect("GMAC get_mac failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for GMAC {}
|
|
||||||
|
|
||||||
/// A wrapper for GMAC with an incrementing 96-bit nonce.
|
|
||||||
///
|
|
||||||
/// This is designed for use to authenticate messages on an otherwise unencrypted
|
|
||||||
/// TCP connection. The nonce is treated as a 96-bit little-endian integer that
|
|
||||||
/// is incremented for each message. It should not be used beyond 2^96 messages
|
|
||||||
/// but that's a ludicrously large message count.
|
|
||||||
pub struct GMACStream(GMAC, u128);
|
|
||||||
|
|
||||||
impl GMACStream {
|
|
||||||
/// Create a new streaming GMAC instance.
|
|
||||||
/// Key must be 16, 24, or 32 bytes in length. Initial nonce must be 16 bytes
|
|
||||||
/// in length, though only the first 12 are used. If either of these are not
|
|
||||||
/// sized properly this will panic.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn new(key: &[u8], initial_nonce: &[u8]) -> Self {
|
|
||||||
assert_eq!(initial_nonce.len(), 16);
|
|
||||||
Self(GMAC::new(key), u128::from_ne_bytes(initial_nonce.try_into().unwrap()))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn init_for_next_message(&mut self) {
|
|
||||||
self.0.init(unsafe { &*(&self.1 as *const u128).cast::<[u8; 12]>() });
|
|
||||||
self.1 = u128::from_le(self.1).wrapping_add(1).to_le();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn update(&mut self, data: &[u8]) { self.0.update(data); }
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn finish(&mut self, mac: &mut [u8; 16]) { self.0.finish(mac); }
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for GMACStream {}
|
|
|
@ -6,135 +6,243 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::mem::MaybeUninit;
|
use std::ffi::c_void;
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
use std::os::raw::{c_int, c_uint};
|
||||||
|
use std::ptr::null;
|
||||||
|
|
||||||
pub const SHA512_HASH_SIZE: usize = 64;
|
pub const SHA512_HASH_SIZE: usize = 64;
|
||||||
pub const SHA384_HASH_SIZE: usize = 48;
|
pub const SHA384_HASH_SIZE: usize = 48;
|
||||||
|
|
||||||
pub struct SHA512(gcrypt::digest::MessageDigest);
|
pub struct SHA512(Option<openssl::sha::Sha512>);
|
||||||
|
|
||||||
impl SHA512 {
|
impl SHA512 {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn hash(b: &[u8]) -> [u8; SHA512_HASH_SIZE] {
|
pub fn hash(b: &[u8]) -> [u8; SHA512_HASH_SIZE] {
|
||||||
let mut h = unsafe { MaybeUninit::<[u8; SHA512_HASH_SIZE]>::uninit().assume_init() };
|
openssl::sha::sha512(b)
|
||||||
gcrypt::digest::hash(gcrypt::digest::Algorithm::Sha512, b, &mut h);
|
|
||||||
h
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hmac(key: &[u8], msg: &[u8]) -> [u8; SHA512_HASH_SIZE] {
|
|
||||||
let mut m = gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::HmacSha512).unwrap();
|
|
||||||
m.set_key(key).expect("FATAL: invalid HMAC-SHA512 key");
|
|
||||||
m.update(msg).expect("FATAL: HMAC-SHA512 failed");
|
|
||||||
let mut h = [0_u8; SHA512_HASH_SIZE];
|
|
||||||
m.get_mac(&mut h).expect("FATAL: HMAC-SHA512 failed");
|
|
||||||
h
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hmac_multipart(key: &[u8], msg: &[&[u8]]) -> [u8; SHA512_HASH_SIZE] {
|
|
||||||
let mut m = gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::HmacSha512).unwrap();
|
|
||||||
m.set_key(key).expect("FATAL: invalid HMAC-SHA512 key");
|
|
||||||
for msg_part in msg.iter() {
|
|
||||||
m.update(*msg_part).expect("FATAL: HMAC-SHA512 failed");
|
|
||||||
}
|
|
||||||
let mut h = [0_u8; SHA512_HASH_SIZE];
|
|
||||||
m.get_mac(&mut h).expect("FATAL: HMAC-SHA512 failed");
|
|
||||||
h
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self { Self(gcrypt::digest::MessageDigest::new(gcrypt::digest::Algorithm::Sha512).unwrap()) }
|
pub fn new() -> Self {
|
||||||
|
Self(Some(openssl::sha::Sha512::new()))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reset(&mut self) { self.0.reset(); }
|
pub fn reset(&mut self) {
|
||||||
|
let _ = self.0.replace(openssl::sha::Sha512::new());
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn update(&mut self, b: &[u8]) { self.0.update(b); }
|
pub fn update(&mut self, b: &[u8]) {
|
||||||
|
self.0.as_mut().unwrap().update(b);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn finish(&mut self) -> [u8; SHA512_HASH_SIZE] {
|
pub fn finish(&mut self) -> [u8; SHA512_HASH_SIZE] {
|
||||||
self.0.finish();
|
self.0.take().unwrap().finish()
|
||||||
self.0.get_only_digest().unwrap().try_into().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn finish_get_ref(&mut self) -> &[u8] {
|
|
||||||
self.0.finish();
|
|
||||||
self.0.get_only_digest().unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for SHA512 {
|
impl Write for SHA512 {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write(&mut self, b: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, b: &[u8]) -> std::io::Result<usize> {
|
||||||
self.0.update(b);
|
self.0.as_mut().unwrap().update(b);
|
||||||
Ok(b.len())
|
Ok(b.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn flush(&mut self) -> std::io::Result<()> { self.0.flush() }
|
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SHA384(gcrypt::digest::MessageDigest);
|
pub struct SHA384(Option<openssl::sha::Sha384>);
|
||||||
|
|
||||||
impl SHA384 {
|
impl SHA384 {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn hash(b: &[u8]) -> [u8; SHA384_HASH_SIZE] {
|
pub fn hash(b: &[u8]) -> [u8; SHA384_HASH_SIZE] {
|
||||||
let mut h = unsafe { MaybeUninit::<[u8; SHA384_HASH_SIZE]>::uninit().assume_init() };
|
openssl::sha::sha384(b)
|
||||||
gcrypt::digest::hash(gcrypt::digest::Algorithm::Sha384, b, &mut h);
|
|
||||||
h
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hmac(key: &[u8], msg: &[u8]) -> [u8; SHA384_HASH_SIZE] {
|
|
||||||
let mut m = gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::HmacSha384).unwrap();
|
|
||||||
m.set_key(key).expect("FATAL: invalid HMAC-SHA384 key");
|
|
||||||
m.update(msg).expect("FATAL: HMAC-SHA384 failed");
|
|
||||||
let mut h = [0_u8; SHA384_HASH_SIZE];
|
|
||||||
m.get_mac(&mut h).expect("FATAL: HMAC-SHA384 failed");
|
|
||||||
h
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hmac_multipart(key: &[u8], msg: &[&[u8]]) -> [u8; SHA384_HASH_SIZE] {
|
|
||||||
let mut m = gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::HmacSha384).unwrap();
|
|
||||||
m.set_key(key).expect("FATAL: invalid HMAC-SHA512 key");
|
|
||||||
for msg_part in msg.iter() {
|
|
||||||
m.update(*msg_part).expect("FATAL: HMAC-SHA512 failed");
|
|
||||||
}
|
|
||||||
let mut h = [0_u8; SHA384_HASH_SIZE];
|
|
||||||
m.get_mac(&mut h).expect("FATAL: HMAC-SHA512 failed");
|
|
||||||
h
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new() -> Self { Self(gcrypt::digest::MessageDigest::new(gcrypt::digest::Algorithm::Sha384).unwrap()) }
|
pub fn new() -> Self {
|
||||||
|
Self(Some(openssl::sha::Sha384::new()))
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn reset(&mut self) { self.0.reset(); }
|
pub fn reset(&mut self) {
|
||||||
|
let _ = self.0.replace(openssl::sha::Sha384::new());
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn update(&mut self, b: &[u8]) { self.0.update(b); }
|
pub fn update(&mut self, b: &[u8]) {
|
||||||
|
self.0.as_mut().unwrap().update(b);
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn finish(&mut self) -> [u8; SHA384_HASH_SIZE] {
|
pub fn finish(&mut self) -> [u8; SHA384_HASH_SIZE] {
|
||||||
self.0.finish();
|
self.0.take().unwrap().finish()
|
||||||
self.0.get_only_digest().unwrap().try_into().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn finish_get_ref(&mut self) -> &[u8] {
|
|
||||||
self.0.finish();
|
|
||||||
self.0.get_only_digest().unwrap()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Write for SHA384 {
|
impl Write for SHA384 {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn write(&mut self, b: &[u8]) -> std::io::Result<usize> {
|
fn write(&mut self, b: &[u8]) -> std::io::Result<usize> {
|
||||||
self.0.update(b);
|
self.0.as_mut().unwrap().update(b);
|
||||||
Ok(b.len())
|
Ok(b.len())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn flush(&mut self) -> std::io::Result<()> { self.0.flush() }
|
fn flush(&mut self) -> std::io::Result<()> { Ok(()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
//#[link(name="crypto")]
|
||||||
|
extern "C" {
|
||||||
|
fn HMAC_CTX_new() -> *mut c_void;
|
||||||
|
fn HMAC_CTX_reset(ctx: *mut c_void) -> c_int;
|
||||||
|
fn HMAC_Init_ex(ctx: *mut c_void, key: *const c_void, key_len: c_int, evp_md: *const c_void, _impl: *const c_void) -> c_int;
|
||||||
|
fn HMAC_Update(ctx: *mut c_void, data: *const c_void, len: usize) -> c_int;
|
||||||
|
fn HMAC_Final(ctx: *mut c_void, output: *mut c_void, output_len: *mut c_uint) -> c_int;
|
||||||
|
fn HMAC_CTX_free(ctx: *mut c_void);
|
||||||
|
fn EVP_sha384() -> *const c_void;
|
||||||
|
fn EVP_sha512() -> *const c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct HMACSHA512 {
|
||||||
|
ctx: *mut c_void,
|
||||||
|
evp_md: *const c_void
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HMACSHA512 {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new(key: &[u8]) -> Self {
|
||||||
|
unsafe {
|
||||||
|
let hm = Self {
|
||||||
|
ctx: HMAC_CTX_new(),
|
||||||
|
evp_md: EVP_sha512()
|
||||||
|
};
|
||||||
|
assert!(!hm.ctx.is_null());
|
||||||
|
assert_ne!(HMAC_Init_ex(hm.ctx, key.as_ptr().cast(), key.len() as c_int, hm.evp_md, null()), 0);
|
||||||
|
hm
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
assert_ne!(HMAC_CTX_reset(self.ctx), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn update(&mut self, b: &[u8]) {
|
||||||
|
unsafe {
|
||||||
|
assert_ne!(HMAC_Update(self.ctx, b.as_ptr().cast(), b.len()), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn finish_into(&mut self, md: &mut [u8]) {
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(md.len(), 64);
|
||||||
|
let mut mdlen: c_uint = 64;
|
||||||
|
assert_ne!(HMAC_Final(self.ctx, md.as_mut_ptr().cast(), &mut mdlen), 0);
|
||||||
|
assert_eq!(mdlen, 64);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn finish(&mut self) -> [u8; 64] {
|
||||||
|
unsafe {
|
||||||
|
let mut tmp: [u8; 64] = MaybeUninit::uninit().assume_init();
|
||||||
|
self.finish_into(&mut tmp);
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for HMACSHA512 {
|
||||||
|
#[inline(always)]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { HMAC_CTX_free(self.ctx) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for HMACSHA512 {}
|
||||||
|
|
||||||
|
pub struct HMACSHA384 {
|
||||||
|
ctx: *mut c_void,
|
||||||
|
evp_md: *const c_void
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HMACSHA384 {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn new(key: &[u8]) -> Self {
|
||||||
|
unsafe {
|
||||||
|
let hm = Self {
|
||||||
|
ctx: HMAC_CTX_new(),
|
||||||
|
evp_md: EVP_sha384()
|
||||||
|
};
|
||||||
|
assert!(!hm.ctx.is_null());
|
||||||
|
assert_ne!(HMAC_Init_ex(hm.ctx, key.as_ptr().cast(), key.len() as c_int, hm.evp_md, null()), 0);
|
||||||
|
hm
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn reset(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
assert_ne!(HMAC_CTX_reset(self.ctx), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn update(&mut self, b: &[u8]) {
|
||||||
|
unsafe {
|
||||||
|
assert_ne!(HMAC_Update(self.ctx, b.as_ptr().cast(), b.len()), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn finish_into(&mut self, md: &mut [u8]) {
|
||||||
|
unsafe {
|
||||||
|
assert_eq!(md.len(), 48);
|
||||||
|
let mut mdlen: c_uint = 48;
|
||||||
|
assert_ne!(HMAC_Final(self.ctx, md.as_mut_ptr().cast(), &mut mdlen), 0);
|
||||||
|
assert_eq!(mdlen, 48);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn finish(&mut self) -> [u8; 48] {
|
||||||
|
unsafe {
|
||||||
|
let mut tmp: [u8; 48] = MaybeUninit::uninit().assume_init();
|
||||||
|
self.finish_into(&mut tmp);
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for HMACSHA384 {
|
||||||
|
#[inline(always)]
|
||||||
|
fn drop(&mut self) {
|
||||||
|
unsafe { HMAC_CTX_free(self.ctx) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for HMACSHA384 {}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn hmac_sha512(key: &[u8], msg: &[u8]) -> [u8; 64] {
|
||||||
|
let mut hm = HMACSHA512::new(key);
|
||||||
|
hm.update(msg);
|
||||||
|
hm.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn hmac_sha384(key: &[u8], msg: &[u8]) -> [u8; 48] {
|
||||||
|
let mut hm = HMACSHA384::new(key);
|
||||||
|
hm.update(msg);
|
||||||
|
hm.finish()
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,31 +6,36 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use crate::hash::{SHA384, SHA384_HASH_SIZE};
|
|
||||||
use crate::secret::Secret;
|
use crate::secret::Secret;
|
||||||
|
|
||||||
/// Derive a key using KBKDF prefaced by the bytes 'ZT' for use in ZeroTier.
|
pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<48> {
|
||||||
/// This is a fixed cost key derivation function used to derive sub-keys from a single original
|
|
||||||
/// shared secret for different uses, such as the K0/K1 in AES-GMAC-SIV.
|
|
||||||
/// Key must be 384 bits in length.
|
|
||||||
pub fn zt_kbkdf_hmac_sha384(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<{ SHA384_HASH_SIZE }> {
|
|
||||||
debug_assert_eq!(key.len(), SHA384_HASH_SIZE);
|
|
||||||
|
|
||||||
// HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4]
|
// HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4]
|
||||||
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf
|
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf page 12
|
||||||
Secret(SHA384::hmac(key, &[
|
Secret(crate::hash::hmac_sha384(key, &[
|
||||||
(iter >> 24) as u8,
|
(iter >> 24) as u8,
|
||||||
(iter >> 16) as u8,
|
(iter >> 16) as u8,
|
||||||
(iter >> 8) as u8,
|
(iter >> 8) as u8,
|
||||||
iter as u8,
|
iter as u8,
|
||||||
b'Z',
|
b'Z', b'T', // can also be considered part of "label"
|
||||||
b'T',
|
|
||||||
label,
|
label,
|
||||||
0,
|
0,
|
||||||
context,
|
context,
|
||||||
0,
|
0, 0, 0x01, 0x80 // 384 bits
|
||||||
0,
|
]))
|
||||||
0x01,
|
}
|
||||||
0x80
|
|
||||||
|
pub fn zt_kbkdf_hmac_sha512(key: &[u8], label: u8, context: u8, iter: u32) -> Secret<64> {
|
||||||
|
// HMAC'd message is: preface | iteration[4], preface[2], label, 0x00, context, hash size[4]
|
||||||
|
// See: https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf page 12
|
||||||
|
Secret(crate::hash::hmac_sha512(key, &[
|
||||||
|
(iter >> 24) as u8,
|
||||||
|
(iter >> 16) as u8,
|
||||||
|
(iter >> 8) as u8,
|
||||||
|
iter as u8,
|
||||||
|
b'Z', b'T', // can also be considered part of "label"
|
||||||
|
label,
|
||||||
|
0,
|
||||||
|
context,
|
||||||
|
0, 0, 0x01, 0x80 // 384 bits
|
||||||
]))
|
]))
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,17 +8,15 @@
|
||||||
|
|
||||||
pub mod c25519;
|
pub mod c25519;
|
||||||
pub mod hash;
|
pub mod hash;
|
||||||
pub mod p521;
|
pub mod p384;
|
||||||
pub mod salsa;
|
pub mod salsa;
|
||||||
pub mod poly1305;
|
pub mod poly1305;
|
||||||
pub mod balloon;
|
|
||||||
pub mod kbkdf;
|
pub mod kbkdf;
|
||||||
pub mod random;
|
pub mod random;
|
||||||
pub mod secret;
|
pub mod secret;
|
||||||
pub mod hex;
|
pub mod hex;
|
||||||
pub mod varint;
|
pub mod varint;
|
||||||
pub mod sidhp751;
|
pub mod sidhp751;
|
||||||
pub mod gmac;
|
|
||||||
|
|
||||||
pub use aes_gmac_siv;
|
pub use aes_gmac_siv;
|
||||||
pub use rand_core;
|
pub use rand_core;
|
||||||
|
|
234
zerotier-core-crypto/src/p384.rs
Normal file
234
zerotier-core-crypto/src/p384.rs
Normal file
|
@ -0,0 +1,234 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||||
|
*
|
||||||
|
* (c)2021 ZeroTier, Inc.
|
||||||
|
* https://www.zerotier.com/
|
||||||
|
*/
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
|
use std::os::raw::{c_int, c_ulong, c_void};
|
||||||
|
use std::ptr::{null, write_volatile};
|
||||||
|
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
use openssl::bn::{BigNum, BigNumContext};
|
||||||
|
use openssl::ec::{EcKey, EcPoint, EcPointRef, PointConversionForm};
|
||||||
|
use openssl::ecdsa::EcdsaSig;
|
||||||
|
use openssl::nid::Nid;
|
||||||
|
use openssl::pkey::{Private, Public};
|
||||||
|
use foreign_types::{ForeignType, ForeignTypeRef};
|
||||||
|
|
||||||
|
use crate::hash::SHA384;
|
||||||
|
use crate::secret::Secret;
|
||||||
|
|
||||||
|
pub const P384_PUBLIC_KEY_SIZE: usize = 49;
|
||||||
|
pub const P384_SECRET_KEY_SIZE: usize = 48;
|
||||||
|
pub const P384_ECDSA_SIGNATURE_SIZE: usize = 96;
|
||||||
|
pub const P384_ECDH_SHARED_SECRET_SIZE: usize = 48;
|
||||||
|
|
||||||
|
//#[link(name="crypto")]
|
||||||
|
extern "C" {
|
||||||
|
fn ECDH_compute_key(out: *mut c_void, outlen: c_ulong, pub_key: *mut c_void, ecdh: *mut c_void, kdf: *const c_void) -> c_int;
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref GROUP_P384: openssl::ec::EcGroup = openssl::ec::EcGroup::from_curve_name(Nid::SECP384R1).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A NIST P-384 ECDH/ECDSA public key.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct P384PublicKey {
|
||||||
|
key: EcKey<Public>,
|
||||||
|
bytes: [u8; 49]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl P384PublicKey {
|
||||||
|
fn new_from_point(key: &EcPointRef) -> Self {
|
||||||
|
let mut bnc = BigNumContext::new().unwrap();
|
||||||
|
let kb = key.to_bytes(GROUP_P384.as_ref(), PointConversionForm::COMPRESSED, &mut bnc).unwrap();
|
||||||
|
let mut bytes = [0_u8; 49];
|
||||||
|
bytes[(49 - kb.len())..].copy_from_slice(kb.as_slice());
|
||||||
|
Self {
|
||||||
|
key: EcKey::from_public_key(GROUP_P384.as_ref(), key).unwrap(),
|
||||||
|
bytes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(b: &[u8]) -> Option<P384PublicKey> {
|
||||||
|
if b.len() == 49 {
|
||||||
|
let mut bnc = BigNumContext::new().unwrap();
|
||||||
|
let key = EcPoint::from_bytes(GROUP_P384.as_ref(), b, &mut bnc);
|
||||||
|
if key.is_ok() {
|
||||||
|
let key = key.unwrap();
|
||||||
|
if key.is_on_curve(GROUP_P384.as_ref(), &mut bnc).unwrap_or(false) {
|
||||||
|
let key = EcKey::from_public_key(GROUP_P384.as_ref(), key.as_ref());
|
||||||
|
if key.is_ok() {
|
||||||
|
return Some(Self {
|
||||||
|
key: key.unwrap(),
|
||||||
|
bytes: b.try_into().unwrap()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn verify(&self, msg: &[u8], signature: &[u8]) -> bool {
|
||||||
|
if signature.len() == 96 {
|
||||||
|
let r = BigNum::from_slice(&signature[0..48]);
|
||||||
|
let s = BigNum::from_slice(&signature[48..96]);
|
||||||
|
if r.is_ok() && s.is_ok() {
|
||||||
|
let sig = EcdsaSig::from_private_components(r.unwrap(), s.unwrap());
|
||||||
|
if sig.is_ok() {
|
||||||
|
return sig.unwrap().verify(&SHA384::hash(msg), self.key.as_ref()).unwrap_or(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn as_bytes(&self) -> &[u8; 49] { &self.bytes }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for P384PublicKey {
|
||||||
|
#[inline(always)]
|
||||||
|
fn eq(&self, other: &Self) -> bool { self.bytes == other.bytes }
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe impl Send for P384PublicKey {}
|
||||||
|
|
||||||
|
unsafe impl Sync for P384PublicKey {}
|
||||||
|
|
||||||
|
/// A NIST P-384 ECDH/ECDSA public/private key pair.
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct P384KeyPair {
|
||||||
|
pair: EcKey<Private>,
|
||||||
|
public: P384PublicKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl P384KeyPair {
|
||||||
|
pub fn generate() -> P384KeyPair {
|
||||||
|
let pair = EcKey::generate(GROUP_P384.as_ref()).unwrap(); // failure implies a serious problem
|
||||||
|
assert!(pair.check_key().is_ok()); // also would imply a serious problem
|
||||||
|
let public = P384PublicKey::new_from_point(pair.public_key());
|
||||||
|
Self {
|
||||||
|
pair,
|
||||||
|
public,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option<P384KeyPair> {
|
||||||
|
if public_bytes.len() == 49 && secret_bytes.len() == 48 {
|
||||||
|
P384PublicKey::from_bytes(public_bytes).map_or(None, |public| {
|
||||||
|
BigNum::from_slice(secret_bytes).map_or(None, |private| {
|
||||||
|
let pair = EcKey::from_private_components(GROUP_P384.as_ref(), private.as_ref(), public.key.public_key());
|
||||||
|
if pair.is_ok() {
|
||||||
|
let pair = pair.unwrap();
|
||||||
|
if pair.check_key().is_ok() {
|
||||||
|
Some(Self {
|
||||||
|
pair,
|
||||||
|
public
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn public_key(&self) -> &P384PublicKey { &self.public }
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn public_key_bytes(&self) -> &[u8; P384_PUBLIC_KEY_SIZE] { &self.public.bytes }
|
||||||
|
|
||||||
|
pub fn secret_key_bytes(&self) -> Secret<P384_SECRET_KEY_SIZE> {
|
||||||
|
let mut tmp: Secret<P384_SECRET_KEY_SIZE> = Secret::default();
|
||||||
|
let mut k = self.pair.private_key().to_vec();
|
||||||
|
tmp.0[(48 - k.len())..].copy_from_slice(k.as_slice());
|
||||||
|
unsafe {
|
||||||
|
// Force zero memory occupied by temporary vector before releasing.
|
||||||
|
let kp = k.as_mut_ptr();
|
||||||
|
for i in 0..k.len() {
|
||||||
|
write_volatile(kp.add(i), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign a message with ECDSA/SHA384.
|
||||||
|
pub fn sign(&self, msg: &[u8]) -> [u8; P384_ECDSA_SIGNATURE_SIZE] {
|
||||||
|
let sig = EcdsaSig::sign(&SHA384::hash(msg), self.pair.as_ref()).unwrap();
|
||||||
|
let r = sig.r().to_vec();
|
||||||
|
let s = sig.s().to_vec();
|
||||||
|
assert!(!r.is_empty() && !s.is_empty() && r.len() <= 48 && s.len() <= 48);
|
||||||
|
let mut b = [0_u8; P384_ECDSA_SIGNATURE_SIZE];
|
||||||
|
b[(48 - r.len())..48].copy_from_slice(r.as_slice());
|
||||||
|
b[(96 - s.len())..96].copy_from_slice(s.as_slice());
|
||||||
|
b
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Perform ECDH key agreement, returning the raw (un-hashed!) ECDH secret.
|
||||||
|
pub fn agree(&self, other_public: &P384PublicKey) -> Option<Secret<P384_ECDH_SHARED_SECRET_SIZE>> {
|
||||||
|
unsafe {
|
||||||
|
let mut s: Secret<P384_ECDH_SHARED_SECRET_SIZE> = Secret::default();
|
||||||
|
if ECDH_compute_key(s.0.as_mut_ptr().cast(), 48, other_public.key.public_key().as_ptr().cast(), self.pair.as_ptr().cast(), null()) == 48 {
|
||||||
|
Some(s)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for P384KeyPair {
|
||||||
|
#[inline(always)]
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.pair.private_key().eq(other.pair.private_key()) && self.public.bytes.eq(&other.public.bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Eq for P384KeyPair {}
|
||||||
|
|
||||||
|
unsafe impl Send for P384KeyPair {}
|
||||||
|
|
||||||
|
unsafe impl Sync for P384KeyPair {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::p384::P384KeyPair;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn generate_sign_verify_agree() {
|
||||||
|
let kp = P384KeyPair::generate();
|
||||||
|
let kp2 = P384KeyPair::generate();
|
||||||
|
|
||||||
|
let sig = kp.sign(&[0_u8; 16]);
|
||||||
|
if !kp.public_key().verify(&[0_u8; 16], &sig) {
|
||||||
|
panic!("ECDSA verify failed");
|
||||||
|
}
|
||||||
|
if kp.public_key().verify(&[1_u8; 16], &sig) {
|
||||||
|
panic!("ECDSA verify succeeded for incorrect message");
|
||||||
|
}
|
||||||
|
|
||||||
|
let sec0 = kp.agree(kp2.public_key()).unwrap();
|
||||||
|
let sec1 = kp2.agree(kp.public_key()).unwrap();
|
||||||
|
if !sec0.eq(&sec1) {
|
||||||
|
panic!("ECDH secrets do not match");
|
||||||
|
}
|
||||||
|
|
||||||
|
let kp3 = P384KeyPair::from_bytes(kp.public_key_bytes(), kp.secret_key_bytes().as_ref()).unwrap();
|
||||||
|
let sig = kp3.sign(&[3_u8; 16]);
|
||||||
|
if !kp.public_key().verify(&[3_u8; 16], &sig) {
|
||||||
|
panic!("ECDSA verify failed (from key reconstructed from bytes)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,287 +0,0 @@
|
||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
||||||
*
|
|
||||||
* (c)2021 ZeroTier, Inc.
|
|
||||||
* https://www.zerotier.com/
|
|
||||||
*/
|
|
||||||
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use gcrypt::sexp::SExpression;
|
|
||||||
|
|
||||||
use crate::secret::Secret;
|
|
||||||
|
|
||||||
pub const P521_PUBLIC_KEY_SIZE: usize = 132;
|
|
||||||
pub const P521_SECRET_KEY_SIZE: usize = 66;
|
|
||||||
pub const P521_ECDSA_SIGNATURE_SIZE: usize = 132;
|
|
||||||
pub const P521_ECDH_SHARED_SECRET_SIZE: usize = 132;
|
|
||||||
|
|
||||||
/*
|
|
||||||
fn dump_sexp(exp: &SExpression) {
|
|
||||||
if exp.len() == 1 {
|
|
||||||
let s = exp.get_str(0);
|
|
||||||
if s.is_ok() {
|
|
||||||
print!("{}", s.unwrap());
|
|
||||||
} else {
|
|
||||||
let b = exp.get_bytes(0);
|
|
||||||
if b.is_some() {
|
|
||||||
print!("#{}#", crate::hex::to_string(b.unwrap()));
|
|
||||||
} else {
|
|
||||||
print!("()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if exp.len() > 0 {
|
|
||||||
for i in 0..exp.len() {
|
|
||||||
let v = exp.get(i as u32);
|
|
||||||
if v.is_some() {
|
|
||||||
if i == 0 {
|
|
||||||
print!("(");
|
|
||||||
} else {
|
|
||||||
print!(" ");
|
|
||||||
}
|
|
||||||
dump_sexp(&v.unwrap());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
print!(")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
fn hash_to_data_sexp(msg: &[u8]) -> [u8; 155] {
|
|
||||||
let h = crate::hash::SHA512::hash(msg);
|
|
||||||
let mut d = [0_u8; 155];
|
|
||||||
d[0..24].copy_from_slice(b"(data(flags raw)(value #");
|
|
||||||
let mut j = 24;
|
|
||||||
for i in 0..64 {
|
|
||||||
let b = h[i] as usize;
|
|
||||||
d[j] = crate::hex::HEX_CHARS[b >> 4];
|
|
||||||
d[j + 1] = crate::hex::HEX_CHARS[b & 0xf];
|
|
||||||
j += 2;
|
|
||||||
}
|
|
||||||
d[152..155].copy_from_slice(b"#))");
|
|
||||||
d
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct P521PublicKey {
|
|
||||||
public_key: SExpression,
|
|
||||||
public_key_bytes: [u8; P521_PUBLIC_KEY_SIZE],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl P521PublicKey {
|
|
||||||
/// Construct a public key from a byte serialized representation.
|
|
||||||
/// None is returned if the input is not valid. No advanced checking such as
|
|
||||||
/// determining if this is a point on the curve is performed.
|
|
||||||
pub fn from_bytes(b: &[u8]) -> Option<P521PublicKey> {
|
|
||||||
if b.len() == P521_PUBLIC_KEY_SIZE {
|
|
||||||
Some(P521PublicKey {
|
|
||||||
public_key: SExpression::from_str(format!("(public-key(ecc(curve nistp521)(q #04{}#)))", crate::hex::to_string(b)).as_str()).unwrap(),
|
|
||||||
public_key_bytes: b.try_into().unwrap(),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Verify a signature.
|
|
||||||
/// Message data does not need to be pre-hashed.
|
|
||||||
pub fn verify(&self, msg: &[u8], signature: &[u8]) -> bool {
|
|
||||||
if signature.len() == P521_ECDSA_SIGNATURE_SIZE {
|
|
||||||
let data = SExpression::from_str(unsafe { std::str::from_utf8_unchecked(&hash_to_data_sexp(msg)) }).unwrap();
|
|
||||||
let sig = SExpression::from_str(format!("(sig-val(ecdsa(r #{}#)(s #{}#)))", crate::hex::to_string(&signature[0..66]), crate::hex::to_string(&signature[66..132])).as_str()).unwrap();
|
|
||||||
gcrypt::pkey::verify(&self.public_key, &data, &sig).is_ok()
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn public_key_bytes(&self) -> &[u8; P521_PUBLIC_KEY_SIZE] {
|
|
||||||
&self.public_key_bytes
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for P521PublicKey {
|
|
||||||
#[inline(always)]
|
|
||||||
fn eq(&self, other: &Self) -> bool { self.public_key_bytes.eq(&other.public_key_bytes) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for P521PublicKey {}
|
|
||||||
|
|
||||||
impl Clone for P521PublicKey {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
public_key: SExpression::from_bytes(self.public_key.get_bytes(0).unwrap()).unwrap(),
|
|
||||||
public_key_bytes: self.public_key_bytes.clone()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for P521PublicKey {}
|
|
||||||
|
|
||||||
unsafe impl Sync for P521PublicKey {}
|
|
||||||
|
|
||||||
/// NIST P-521 elliptic curve key pair.
|
|
||||||
/// This supports both ECDSA signing and ECDH key agreement. In practice the same key pair
|
|
||||||
/// is not used for both functions as this is considred bad practice.
|
|
||||||
pub struct P521KeyPair {
|
|
||||||
public_key: P521PublicKey,
|
|
||||||
secret_key_for_ecdsa: SExpression, // secret key as a private-key S-expression
|
|
||||||
secret_key_for_ecdh: SExpression, // the same secret key as a "data" S-expression for the weird gcrypt ECDH interface
|
|
||||||
secret_key_bytes: Secret<{ P521_SECRET_KEY_SIZE }>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl P521KeyPair {
|
|
||||||
/// Generate a NIST P-521 key pair.
|
|
||||||
/// If transient is true a faster but possibly somewhat less intensive pseudo-random number
|
|
||||||
/// generator is used. This is for ephemeral keys, and has no effect on some platforms.
|
|
||||||
pub fn generate(transient: bool) -> Option<P521KeyPair> {
|
|
||||||
let sexp = SExpression::from_str(if transient { "(genkey(ecc(curve nistp521)(flags nocomp transient-key)))" } else { "(genkey(ecc(curve nistp521)(flags nocomp)))" }).unwrap();
|
|
||||||
gcrypt::pkey::generate_key(&sexp).map_or(None, |kp| {
|
|
||||||
let pk_exp = kp.find_token("public-key");
|
|
||||||
let sk_exp = kp.find_token("private-key");
|
|
||||||
if pk_exp.is_some() && sk_exp.is_some() {
|
|
||||||
let pk_exp = pk_exp.unwrap();
|
|
||||||
let sk_exp = sk_exp.unwrap();
|
|
||||||
let pk = pk_exp.find_token("q");
|
|
||||||
let sk = sk_exp.find_token("d");
|
|
||||||
if pk.is_some() && sk.is_some() {
|
|
||||||
let pktmp = pk.unwrap();
|
|
||||||
let sktmp = sk.unwrap();
|
|
||||||
let pk = pktmp.get_bytes(1);
|
|
||||||
let sk = sktmp.get_bytes(1);
|
|
||||||
if pk.is_some() && sk.is_some() {
|
|
||||||
let pk = pk.unwrap();
|
|
||||||
let sk = sk.unwrap();
|
|
||||||
let mut kp = P521KeyPair {
|
|
||||||
public_key: P521PublicKey {
|
|
||||||
public_key: pk_exp,
|
|
||||||
public_key_bytes: [0_u8; P521_PUBLIC_KEY_SIZE],
|
|
||||||
},
|
|
||||||
secret_key_for_ecdsa: SExpression::from_str(format!("(private-key(ecc(curve nistp521)(q #{}#)(d #{}#)))", crate::hex::to_string(pk), crate::hex::to_string(sk)).as_str()).unwrap(),
|
|
||||||
secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::hex::to_string(sk)).as_str()).unwrap(),
|
|
||||||
secret_key_bytes: Secret::default(),
|
|
||||||
};
|
|
||||||
kp.public_key.public_key_bytes[((P521_PUBLIC_KEY_SIZE + 1) - pk.len())..P521_PUBLIC_KEY_SIZE].copy_from_slice(&pk[1..]);
|
|
||||||
kp.secret_key_bytes.0[(P521_SECRET_KEY_SIZE - sk.len())..P521_SECRET_KEY_SIZE].copy_from_slice(sk);
|
|
||||||
return Some(kp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return None;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct this key pair from both a public and a private key.
|
|
||||||
pub fn from_bytes(public_bytes: &[u8], secret_bytes: &[u8]) -> Option<P521KeyPair> {
|
|
||||||
if secret_bytes.len() != P521_SECRET_KEY_SIZE {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let public_key = P521PublicKey::from_bytes(public_bytes);
|
|
||||||
if public_key.is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(P521KeyPair {
|
|
||||||
public_key: public_key.unwrap(),
|
|
||||||
secret_key_for_ecdsa: SExpression::from_str(format!("(private-key(ecc(curve nistp521)(q #04{}#)(d #{}#)))", crate::hex::to_string(public_bytes), crate::hex::to_string(secret_bytes)).as_str()).unwrap(),
|
|
||||||
secret_key_for_ecdh: SExpression::from_str(format!("(data(flags raw)(value #{}#))", crate::hex::to_string(secret_bytes)).as_str()).unwrap(),
|
|
||||||
secret_key_bytes: Secret::from_bytes(secret_bytes),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn public_key(&self) -> &P521PublicKey { &self.public_key }
|
|
||||||
|
|
||||||
/// Get the raw ECC public "q" point for this key pair.
|
|
||||||
/// The returned point is not compressed. To use this with other interfaces that expect a format
|
|
||||||
/// prefix, prepend 0x04 to the beginning of this public key. This prefix is always the same in
|
|
||||||
/// our system and so is omitted.
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn public_key_bytes(&self) -> &[u8; P521_PUBLIC_KEY_SIZE] { &self.public_key.public_key_bytes }
|
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn secret_key_bytes(&self) -> &Secret<{ P521_SECRET_KEY_SIZE }> { &self.secret_key_bytes }
|
|
||||||
|
|
||||||
/// Create an ECDSA signature of the input message.
|
|
||||||
/// Message data does not need to be pre-hashed.
|
|
||||||
pub fn sign(&self, msg: &[u8]) -> Option<[u8; P521_ECDSA_SIGNATURE_SIZE]> {
|
|
||||||
let data = SExpression::from_str(unsafe { std::str::from_utf8_unchecked(&hash_to_data_sexp(msg)) }).unwrap();
|
|
||||||
gcrypt::pkey::sign(&self.secret_key_for_ecdsa, &data).map_or(None, |sig| {
|
|
||||||
let mut sig_bytes = [0_u8; P521_ECDSA_SIGNATURE_SIZE];
|
|
||||||
if sig.find_token("r").map_or(false, |r| r.get_bytes(1).map_or(false, |r| {
|
|
||||||
sig_bytes[(66 - r.len())..66].copy_from_slice(r);
|
|
||||||
true
|
|
||||||
} )) && sig.find_token("s").map_or(false, |s| s.get_bytes(1).map_or(false, |s| {
|
|
||||||
sig_bytes[(66 + (66 - s.len()))..132].copy_from_slice(s);
|
|
||||||
true
|
|
||||||
})) {
|
|
||||||
Some(sig_bytes)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Execute ECDH key agreement, returning a raw (un-hashed) shared secret.
|
|
||||||
pub fn agree(&self, other_public: &P521PublicKey) -> Option<Secret<{ P521_ECDH_SHARED_SECRET_SIZE }>> {
|
|
||||||
gcrypt::pkey::encrypt(&other_public.public_key, &self.secret_key_for_ecdh).map_or(None, |k| {
|
|
||||||
k.find_token("s").map_or(None, |s| s.get_bytes(1).map_or(None, |sb| {
|
|
||||||
Some(Secret(sb[1..].try_into().unwrap()))
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for P521KeyPair {
|
|
||||||
fn eq(&self, other: &Self) -> bool { self.secret_key_bytes.0.eq(&other.secret_key_bytes.0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for P521KeyPair {}
|
|
||||||
|
|
||||||
impl Clone for P521KeyPair {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
|
||||||
public_key: self.public_key.clone(),
|
|
||||||
secret_key_for_ecdsa: SExpression::from_bytes(self.secret_key_for_ecdsa.get_bytes(0).unwrap()).unwrap(),
|
|
||||||
secret_key_for_ecdh: SExpression::from_bytes(self.secret_key_for_ecdh.get_bytes(0).unwrap()).unwrap(),
|
|
||||||
secret_key_bytes: self.secret_key_bytes.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe impl Send for P521KeyPair {}
|
|
||||||
|
|
||||||
unsafe impl Sync for P521KeyPair {}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use crate::p521::P521KeyPair;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn generate_sign_verify_agree() {
|
|
||||||
let kp = P521KeyPair::generate(false).unwrap();
|
|
||||||
let kp2 = P521KeyPair::generate(false).unwrap();
|
|
||||||
|
|
||||||
let sig = kp.sign(&[0_u8]).unwrap();
|
|
||||||
if !kp.public_key().verify(&[0_u8], &sig) {
|
|
||||||
panic!("ECDSA verify failed");
|
|
||||||
}
|
|
||||||
if kp.public_key().verify(&[1_u8], &sig) {
|
|
||||||
panic!("ECDSA verify succeeded for incorrect message");
|
|
||||||
}
|
|
||||||
|
|
||||||
let sec0 = kp.agree(kp2.public_key()).unwrap();
|
|
||||||
let sec1 = kp2.agree(kp.public_key()).unwrap();
|
|
||||||
if !sec0.eq(&sec1) {
|
|
||||||
panic!("ECDH secrets do not match");
|
|
||||||
}
|
|
||||||
|
|
||||||
let kp3 = P521KeyPair::from_bytes(kp.public_key_bytes(), kp.secret_key_bytes().as_ref()).unwrap();
|
|
||||||
let sig = kp3.sign(&[3_u8]).unwrap();
|
|
||||||
if !kp.public_key().verify(&[3_u8], &sig) {
|
|
||||||
panic!("ECDSA verify failed (from key reconstructed from bytes)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,19 +6,20 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
use poly1305::universal_hash::{NewUniversalHash, UniversalHash};
|
||||||
|
|
||||||
/// The poly1305 message authentication function.
|
/// The poly1305 message authentication function.
|
||||||
pub struct Poly1305(gcrypt::mac::Mac);
|
#[repr(transparent)]
|
||||||
|
pub struct Poly1305(poly1305::Poly1305);
|
||||||
|
|
||||||
pub const POLY1305_ONE_TIME_KEY_SIZE: usize = 32;
|
pub const POLY1305_ONE_TIME_KEY_SIZE: usize = 32;
|
||||||
pub const POLY1305_MAC_SIZE: usize = 16;
|
pub const POLY1305_MAC_SIZE: usize = 16;
|
||||||
|
|
||||||
impl Poly1305 {
|
impl Poly1305 {
|
||||||
|
#[inline(always)]
|
||||||
pub fn new(key: &[u8]) -> Option<Poly1305> {
|
pub fn new(key: &[u8]) -> Option<Poly1305> {
|
||||||
if key.len() == 32 {
|
if key.len() == 32 {
|
||||||
gcrypt::mac::Mac::new(gcrypt::mac::Algorithm::Poly1305).map_or(None, |mut poly| {
|
Some(Self(poly1305::Poly1305::new(poly1305::Key::from_slice(key))))
|
||||||
let _ = poly.set_key(key);
|
|
||||||
Some(Poly1305(poly))
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
@ -26,13 +27,11 @@ impl Poly1305 {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn update(&mut self, data: &[u8]) {
|
pub fn update(&mut self, data: &[u8]) {
|
||||||
let _ = self.0.update(data);
|
self.0.update(poly1305::Block::from_slice(data));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn finish(&mut self) -> [u8; POLY1305_MAC_SIZE] {
|
pub fn finish(self) -> [u8; POLY1305_MAC_SIZE] {
|
||||||
let mut mac = [0_u8; POLY1305_MAC_SIZE];
|
self.0.finalize().into_bytes().into()
|
||||||
let _ = self.0.get_mac(&mut mac);
|
|
||||||
mac
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,8 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use gcrypt::rand::{Level, randomize};
|
use openssl::rand::rand_bytes;
|
||||||
|
|
||||||
/// Secure random source based on the desired third party library (gcrypt).
|
|
||||||
pub struct SecureRandom;
|
pub struct SecureRandom;
|
||||||
|
|
||||||
impl SecureRandom {
|
impl SecureRandom {
|
||||||
|
@ -20,24 +19,25 @@ impl rand_core::RngCore for SecureRandom {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn next_u32(&mut self) -> u32 {
|
fn next_u32(&mut self) -> u32 {
|
||||||
let mut tmp = 0_u32;
|
let mut tmp = 0_u32;
|
||||||
randomize(Level::Strong, unsafe { &mut *(&mut tmp as *mut u32).cast::<[u8; 4]>() });
|
assert!(rand_bytes(unsafe { &mut *(&mut tmp as *mut u32).cast::<[u8; 4]>() }).is_ok());
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn next_u64(&mut self) -> u64 {
|
fn next_u64(&mut self) -> u64 {
|
||||||
let mut tmp = 0_u64;
|
let mut tmp = 0_u64;
|
||||||
randomize(Level::Strong, unsafe { &mut *(&mut tmp as *mut u64).cast::<[u8; 8]>() });
|
assert!(rand_bytes(unsafe { &mut *(&mut tmp as *mut u64).cast::<[u8; 8]>() }).is_ok());
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn fill_bytes(&mut self, dest: &mut [u8]) { randomize(Level::Strong, dest); }
|
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||||
|
assert!(rand_bytes(dest).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||||
randomize(Level::Strong, dest);
|
rand_bytes(dest).map_err(|e| rand_core::Error::new(Box::new(e)))
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,19 +46,21 @@ impl rand_core::CryptoRng for SecureRandom {}
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn next_u32_secure() -> u32 {
|
pub fn next_u32_secure() -> u32 {
|
||||||
let mut tmp = 0_u32;
|
let mut tmp = 0_u32;
|
||||||
randomize(Level::Strong, unsafe { &mut *(&mut tmp as *mut u32).cast::<[u8; 4]>() });
|
assert!(rand_bytes(unsafe { &mut *(&mut tmp as *mut u32).cast::<[u8; 4]>() }).is_ok());
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn next_u64_secure() -> u64 {
|
pub fn next_u64_secure() -> u64 {
|
||||||
let mut tmp = 0_u64;
|
let mut tmp = 0_u64;
|
||||||
randomize(Level::Strong, unsafe { &mut *(&mut tmp as *mut u64).cast::<[u8; 8]>() });
|
assert!(rand_bytes(unsafe { &mut *(&mut tmp as *mut u64).cast::<[u8; 8]>() }).is_ok());
|
||||||
tmp
|
tmp
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn fill_bytes_secure(dest: &mut [u8]) { randomize(Level::Strong, dest); }
|
pub fn fill_bytes_secure(dest: &mut [u8]) {
|
||||||
|
assert!(rand_bytes(dest).is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
static mut XORSHIFT64_STATE: u64 = 0;
|
static mut XORSHIFT64_STATE: u64 = 0;
|
||||||
|
|
||||||
|
|
|
@ -6,33 +6,173 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// The classic Salsa20 stream cipher supporting 20-round and 12-round variants.
|
use std::convert::TryInto;
|
||||||
pub struct Salsa(gcrypt::cipher::Cipher);
|
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
|
||||||
|
|
||||||
impl Salsa {
|
const CONSTANTS: [u32; 4] = [ u32::from_le_bytes(*b"expa"), u32::from_le_bytes(*b"nd 3"), u32::from_le_bytes(*b"2-by"), u32::from_le_bytes(*b"te k") ];
|
||||||
/// Initialize Salsa cipher.
|
|
||||||
/// Key must be 32 bytes and iv must be 8 bytes. If r12 is true the 12-round
|
/// Salsa stream cipher implementation supporting 8, 12, or 20 rounds.
|
||||||
/// variant of Salsa will be used, otherwise 20 rounds are used.
|
///
|
||||||
pub fn new(key: &[u8], iv: &[u8], r12: bool) -> Option<Salsa> {
|
/// WARNING: this has a major limitation/caveat. If you call crypt() with plaintext whose
|
||||||
if key.len() == 32 && iv.len() == 8 {
|
/// size is not a multiple of 64, subsequent calls to crypt() will not be properly aligned.
|
||||||
gcrypt::cipher::Cipher::new(if r12 { gcrypt::cipher::Algorithm::Salsa20r12 } else { gcrypt::cipher::Algorithm::Salsa20 }, gcrypt::cipher::Mode::Stream).map_or(None, |mut salsa| {
|
/// This is okay for uses in ZeroTier but might break other cases. Salsa is deprecated as
|
||||||
let _ = salsa.set_key(key);
|
/// transport encryption in ZeroTier anyway, but is still used to derive addresses from
|
||||||
let _ = salsa.set_iv(iv);
|
/// identity public keys.
|
||||||
Some(Salsa(salsa))
|
pub struct Salsa<const ROUNDS: usize> {
|
||||||
})
|
state: [u32; 16]
|
||||||
} else {
|
}
|
||||||
None
|
|
||||||
|
impl<const ROUNDS: usize> Salsa<ROUNDS> {
|
||||||
|
/// Create a new Salsa cipher given a 256-bit key and a 64-bit IV.
|
||||||
|
pub fn new(key: &[u8], iv: &[u8]) -> Self {
|
||||||
|
assert!(ROUNDS == 8 || ROUNDS == 12 || ROUNDS == 20);
|
||||||
|
assert!(key.len() >= 32);
|
||||||
|
assert!(iv.len() >= 8);
|
||||||
|
Self {
|
||||||
|
state: [
|
||||||
|
CONSTANTS[0],
|
||||||
|
u32::from_le_bytes((&key[0..4]).try_into().unwrap()),
|
||||||
|
u32::from_le_bytes((&key[4..8]).try_into().unwrap()),
|
||||||
|
u32::from_le_bytes((&key[8..12]).try_into().unwrap()),
|
||||||
|
u32::from_le_bytes((&key[12..16]).try_into().unwrap()),
|
||||||
|
CONSTANTS[1],
|
||||||
|
u32::from_le_bytes((&iv[0..4]).try_into().unwrap()),
|
||||||
|
u32::from_le_bytes((&iv[4..8]).try_into().unwrap()),
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
CONSTANTS[2],
|
||||||
|
u32::from_le_bytes((&key[16..20]).try_into().unwrap()),
|
||||||
|
u32::from_le_bytes((&key[20..24]).try_into().unwrap()),
|
||||||
|
u32::from_le_bytes((&key[24..28]).try_into().unwrap()),
|
||||||
|
u32::from_le_bytes((&key[28..32]).try_into().unwrap()),
|
||||||
|
CONSTANTS[3]
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Encrypt or decrypt, which for Salsa is the same operation.
|
pub fn crypt(&mut self, mut plaintext: &[u8], mut ciphertext: &mut [u8]) {
|
||||||
#[inline(always)]
|
let (j0, j1, j2, j3, j4, j5, j6, j7, mut j8, mut j9, j10, j11, j12, j13, j14, j15) = (self.state[0], self.state[1], self.state[2], self.state[3], self.state[4], self.state[5], self.state[6], self.state[7], self.state[8], self.state[9], self.state[10], self.state[11], self.state[12], self.state[13], self.state[14], self.state[15]);
|
||||||
pub fn crypt(&mut self, plaintext: &[u8], ciphertext: &mut [u8]) {
|
loop {
|
||||||
let _ = self.0.encrypt(plaintext, ciphertext);
|
let (mut x0, mut x1, mut x2, mut x3, mut x4, mut x5, mut x6, mut x7, mut x8, mut x9, mut x10, mut x11, mut x12, mut x13, mut x14, mut x15) = (j0, j1, j2, j3, j4, j5, j6, j7, j8, j9, j10, j11, j12, j13, j14, j15);
|
||||||
|
|
||||||
|
for _ in 0..(ROUNDS / 2) {
|
||||||
|
x4 ^= x0.wrapping_add(x12).rotate_left(7);
|
||||||
|
x8 ^= x4.wrapping_add(x0).rotate_left(9);
|
||||||
|
x12 ^= x8.wrapping_add(x4).rotate_left(13);
|
||||||
|
x0 ^= x12.wrapping_add(x8).rotate_left(18);
|
||||||
|
x9 ^= x5.wrapping_add(x1).rotate_left(7);
|
||||||
|
x13 ^= x9.wrapping_add(x5).rotate_left(9);
|
||||||
|
x1 ^= x13.wrapping_add(x9).rotate_left(13);
|
||||||
|
x5 ^= x1.wrapping_add(x13).rotate_left(18);
|
||||||
|
x14 ^= x10.wrapping_add(x6).rotate_left(7);
|
||||||
|
x2 ^= x14.wrapping_add(x10).rotate_left(9);
|
||||||
|
x6 ^= x2.wrapping_add(x14).rotate_left(13);
|
||||||
|
x10 ^= x6.wrapping_add(x2).rotate_left(18);
|
||||||
|
x3 ^= x15.wrapping_add(x11).rotate_left(7);
|
||||||
|
x7 ^= x3.wrapping_add(x15).rotate_left(9);
|
||||||
|
x11 ^= x7.wrapping_add(x3).rotate_left(13);
|
||||||
|
x15 ^= x11.wrapping_add(x7).rotate_left(18);
|
||||||
|
x1 ^= x0.wrapping_add(x3).rotate_left(7);
|
||||||
|
x2 ^= x1.wrapping_add(x0).rotate_left(9);
|
||||||
|
x3 ^= x2.wrapping_add(x1).rotate_left(13);
|
||||||
|
x0 ^= x3.wrapping_add(x2).rotate_left(18);
|
||||||
|
x6 ^= x5.wrapping_add(x4).rotate_left(7);
|
||||||
|
x7 ^= x6.wrapping_add(x5).rotate_left(9);
|
||||||
|
x4 ^= x7.wrapping_add(x6).rotate_left(13);
|
||||||
|
x5 ^= x4.wrapping_add(x7).rotate_left(18);
|
||||||
|
x11 ^= x10.wrapping_add(x9).rotate_left(7);
|
||||||
|
x8 ^= x11.wrapping_add(x10).rotate_left(9);
|
||||||
|
x9 ^= x8.wrapping_add(x11).rotate_left(13);
|
||||||
|
x10 ^= x9.wrapping_add(x8).rotate_left(18);
|
||||||
|
x12 ^= x15.wrapping_add(x14).rotate_left(7);
|
||||||
|
x13 ^= x12.wrapping_add(x15).rotate_left(9);
|
||||||
|
x14 ^= x13.wrapping_add(x12).rotate_left(13);
|
||||||
|
x15 ^= x14.wrapping_add(x13).rotate_left(18);
|
||||||
|
}
|
||||||
|
|
||||||
|
x0 = x0.wrapping_add(j0);
|
||||||
|
x1 = x1.wrapping_add(j1);
|
||||||
|
x2 = x2.wrapping_add(j2);
|
||||||
|
x3 = x3.wrapping_add(j3);
|
||||||
|
x4 = x4.wrapping_add(j4);
|
||||||
|
x5 = x5.wrapping_add(j5);
|
||||||
|
x6 = x6.wrapping_add(j6);
|
||||||
|
x7 = x7.wrapping_add(j7);
|
||||||
|
x8 = x8.wrapping_add(j8);
|
||||||
|
x9 = x9.wrapping_add(j9);
|
||||||
|
x10 = x10.wrapping_add(j10);
|
||||||
|
x11 = x11.wrapping_add(j11);
|
||||||
|
x12 = x12.wrapping_add(j12);
|
||||||
|
x13 = x13.wrapping_add(j13);
|
||||||
|
x14 = x14.wrapping_add(j14);
|
||||||
|
x15 = x15.wrapping_add(j15);
|
||||||
|
|
||||||
|
if plaintext.len() >= 64 {
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64"))] {
|
||||||
|
// Slightly faster keystream XOR for little-endian platforms with unaligned load/store.
|
||||||
|
unsafe {
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>() = *plaintext.as_ptr().cast::<u32>() ^ x0;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(1) = *plaintext.as_ptr().cast::<u32>().add(1) ^ x1;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(2) = *plaintext.as_ptr().cast::<u32>().add(2) ^ x2;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(3) = *plaintext.as_ptr().cast::<u32>().add(3) ^ x3;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(4) = *plaintext.as_ptr().cast::<u32>().add(4) ^ x4;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(5) = *plaintext.as_ptr().cast::<u32>().add(5) ^ x5;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(6) = *plaintext.as_ptr().cast::<u32>().add(6) ^ x6;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(7) = *plaintext.as_ptr().cast::<u32>().add(7) ^ x7;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(8) = *plaintext.as_ptr().cast::<u32>().add(8) ^ x8;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(9) = *plaintext.as_ptr().cast::<u32>().add(9) ^ x9;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(10) = *plaintext.as_ptr().cast::<u32>().add(10) ^ x10;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(11) = *plaintext.as_ptr().cast::<u32>().add(11) ^ x11;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(12) = *plaintext.as_ptr().cast::<u32>().add(12) ^ x12;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(13) = *plaintext.as_ptr().cast::<u32>().add(13) ^ x13;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(14) = *plaintext.as_ptr().cast::<u32>().add(14) ^ x14;
|
||||||
|
*ciphertext.as_mut_ptr().cast::<u32>().add(15) = *plaintext.as_ptr().cast::<u32>().add(15) ^ x15;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64", target_arch = "aarch64")))] {
|
||||||
|
// Portable keystream XOR with alignment-safe access and native to little-endian conversion.
|
||||||
|
ciphertext[0..4].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().cast::<[u8; 4]>() }) ^ x0.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[4..8].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(4).cast::<[u8; 4]>() }) ^ x1.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[8..12].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(8).cast::<[u8; 4]>() }) ^ x2.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[12..16].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(12).cast::<[u8; 4]>() }) ^ x3.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[16..20].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(16).cast::<[u8; 4]>() }) ^ x4.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[20..24].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(20).cast::<[u8; 4]>() }) ^ x5.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[24..28].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(24).cast::<[u8; 4]>() }) ^ x6.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[28..32].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(28).cast::<[u8; 4]>() }) ^ x7.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[32..36].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(32).cast::<[u8; 4]>() }) ^ x8.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[36..40].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(36).cast::<[u8; 4]>() }) ^ x9.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[40..44].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(40).cast::<[u8; 4]>() }) ^ x10.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[44..48].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(44).cast::<[u8; 4]>() }) ^ x11.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[48..52].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(48).cast::<[u8; 4]>() }) ^ x12.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[52..56].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(52).cast::<[u8; 4]>() }) ^ x13.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[56..60].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(56).cast::<[u8; 4]>() }) ^ x14.to_le()).to_ne_bytes());
|
||||||
|
ciphertext[60..64].copy_from_slice(&(u32::from_ne_bytes(unsafe { *plaintext.as_ptr().add(60).cast::<[u8; 4]>() }) ^ x15.to_le()).to_ne_bytes());
|
||||||
|
}
|
||||||
|
plaintext = &plaintext[64..];
|
||||||
|
ciphertext = &mut ciphertext[64..];
|
||||||
|
|
||||||
|
j8 = j8.wrapping_add(1);
|
||||||
|
j9 = j9.wrapping_add((j8 == 0) as u32);
|
||||||
|
} else {
|
||||||
|
if !plaintext.is_empty() {
|
||||||
|
let remainder = [x0.to_le(), x1.to_le(), x2.to_le(), x3.to_le(), x4.to_le(), x5.to_le(), x6.to_le(), x7.to_le(), x8.to_le(), x9.to_le(), x10.to_le(), x11.to_le(), x12.to_le(), x13.to_le(), x14.to_le(), x15.to_le()];
|
||||||
|
for i in 0..plaintext.len() {
|
||||||
|
ciphertext[i] = plaintext[i] ^ unsafe { *remainder.as_ptr().cast::<u8>().add(i) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.state[8] = j8;
|
||||||
|
self.state[9] = j9;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn crypt_in_place(&mut self, plaintext_to_ciphertext: &mut [u8]) {
|
pub fn crypt_in_place(&mut self, data: &mut [u8]) {
|
||||||
let _ = self.0.encrypt_inplace(plaintext_to_ciphertext);
|
unsafe {
|
||||||
|
self.crypt(&*slice_from_raw_parts(data.as_ptr(), data.len()), &mut *slice_from_raw_parts_mut(data.as_mut_ptr(), data.len()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::convert::TryInto;
|
|
||||||
use std::ptr::write_volatile;
|
use std::ptr::write_volatile;
|
||||||
|
|
||||||
/// Container for secrets that clears them on drop.
|
/// Container for secrets that clears them on drop.
|
||||||
|
|
|
@ -177,6 +177,7 @@ impl Arbitrary for ExtensionFieldElement {
|
||||||
|
|
||||||
impl ExtensionFieldElement {
|
impl ExtensionFieldElement {
|
||||||
/// Construct a zero `ExtensionFieldElement`.
|
/// Construct a zero `ExtensionFieldElement`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn zero() -> ExtensionFieldElement {
|
pub fn zero() -> ExtensionFieldElement {
|
||||||
ExtensionFieldElement{
|
ExtensionFieldElement{
|
||||||
A: Fp751Element::zero(),
|
A: Fp751Element::zero(),
|
||||||
|
@ -185,6 +186,7 @@ impl ExtensionFieldElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Construct a one `ExtensionFieldElement`.
|
/// Construct a one `ExtensionFieldElement`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn one() -> ExtensionFieldElement {
|
pub fn one() -> ExtensionFieldElement {
|
||||||
ExtensionFieldElement{
|
ExtensionFieldElement{
|
||||||
A: Fp751Element([0x249ad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83100000, 0x375c6c66, 0x5527b1e4, 0x3f4f24d0, 0x697797bf, 0xac5c4e2e, 0xc89db7b2, 0xd2076956, 0x4ca4b439, 0x7512c7e9, 0x10f7926c, 0x24bce5e2, 0x2d5b]),
|
A: Fp751Element([0x249ad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83100000, 0x375c6c66, 0x5527b1e4, 0x3f4f24d0, 0x697797bf, 0xac5c4e2e, 0xc89db7b2, 0xd2076956, 0x4ca4b439, 0x7512c7e9, 0x10f7926c, 0x24bce5e2, 0x2d5b]),
|
||||||
|
@ -234,7 +236,7 @@ impl ExtensionFieldElement {
|
||||||
// Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3).
|
// Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3).
|
||||||
//
|
//
|
||||||
// All xi, yi must be distinct.
|
// All xi, yi must be distinct.
|
||||||
pub fn batch3_inv(x1: &ExtensionFieldElement, x2: &ExtensionFieldElement, x3: &ExtensionFieldElement) ->
|
pub fn batch3_inv(x1: &ExtensionFieldElement, x2: &ExtensionFieldElement, x3: &ExtensionFieldElement) ->
|
||||||
(ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement)
|
(ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement)
|
||||||
{
|
{
|
||||||
let x1x2 = x1 * x2; // x1*x2
|
let x1x2 = x1 * x2; // x1*x2
|
||||||
|
@ -281,6 +283,7 @@ impl ExtensionFieldElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert the input to wire format.
|
/// Convert the input to wire format.
|
||||||
|
#[inline(always)]
|
||||||
pub fn to_bytes(&self) -> [u8; 188] {
|
pub fn to_bytes(&self) -> [u8; 188] {
|
||||||
let mut bytes = [0u8; 188];
|
let mut bytes = [0u8; 188];
|
||||||
bytes[0..94].clone_from_slice(&self.A.to_bytes());
|
bytes[0..94].clone_from_slice(&self.A.to_bytes());
|
||||||
|
@ -289,6 +292,7 @@ impl ExtensionFieldElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read 188 bytes into the given `ExtensionFieldElement`.
|
/// Read 188 bytes into the given `ExtensionFieldElement`.
|
||||||
|
#[inline(always)]
|
||||||
pub fn from_bytes(bytes: &[u8]) -> ExtensionFieldElement {
|
pub fn from_bytes(bytes: &[u8]) -> ExtensionFieldElement {
|
||||||
assert!(bytes.len() >= 188, "Too short input to ExtensionFieldElement from_bytes, expected 188 bytes");
|
assert!(bytes.len() >= 188, "Too short input to ExtensionFieldElement from_bytes, expected 188 bytes");
|
||||||
let a = Fp751Element::from_bytes(&bytes[0..94]);
|
let a = Fp751Element::from_bytes(&bytes[0..94]);
|
||||||
|
|
|
@ -197,7 +197,7 @@ impl<const L: usize> Buffer<L> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn append_varint(&mut self, mut i: u64) -> std::io::Result<()> {
|
pub fn append_varint(&mut self, i: u64) -> std::io::Result<()> {
|
||||||
crate::util::varint::write(self, i)
|
crate::util::varint::write(self, i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
pub mod pool;
|
pub mod pool;
|
||||||
pub mod gate;
|
pub mod gate;
|
||||||
pub mod buffer;
|
pub mod buffer;
|
||||||
pub mod iblt;
|
|
||||||
|
|
||||||
pub use zerotier_core_crypto::hex;
|
pub use zerotier_core_crypto::hex;
|
||||||
pub use zerotier_core_crypto::varint;
|
pub use zerotier_core_crypto::varint;
|
||||||
|
@ -43,9 +42,6 @@ lazy_static! {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn highwayhasher() -> highway::HighwayHasher { highway::HighwayHasher::new(highway::Key(HIGHWAYHASHER_KEY.clone())) }
|
pub(crate) fn highwayhasher() -> highway::HighwayHasher { highway::HighwayHasher::new(highway::Key(HIGHWAYHASHER_KEY.clone())) }
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub(crate) fn u128_from_2xu64_ne(x: [u64; 2]) -> u128 { unsafe { std::mem::transmute(x) } }
|
|
||||||
|
|
||||||
/// Non-cryptographic 64-bit bit mixer for things like local hashing.
|
/// Non-cryptographic 64-bit bit mixer for things like local hashing.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn hash64_noncrypt(mut x: u64) -> u64 {
|
pub(crate) fn hash64_noncrypt(mut x: u64) -> u64 {
|
||||||
|
|
|
@ -6,14 +6,13 @@
|
||||||
* https://www.zerotier.com/
|
* https://www.zerotier.com/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use std::fmt::{Debug, Display};
|
|
||||||
use std::error::Error;
|
|
||||||
use std::sync::atomic::{AtomicU32, Ordering};
|
use std::sync::atomic::{AtomicU32, Ordering};
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
use zerotier_core_crypto::c25519::{C25519KeyPair, C25519_PUBLIC_KEY_SIZE};
|
use zerotier_core_crypto::c25519::{C25519KeyPair, C25519_PUBLIC_KEY_SIZE};
|
||||||
use zerotier_core_crypto::hash::{SHA384_HASH_SIZE, SHA384};
|
use zerotier_core_crypto::hash::*;
|
||||||
use zerotier_core_crypto::p521::{P521KeyPair, P521_PUBLIC_KEY_SIZE, P521PublicKey};
|
use zerotier_core_crypto::kbkdf::zt_kbkdf_hmac_sha512;
|
||||||
|
use zerotier_core_crypto::p384::*;
|
||||||
use zerotier_core_crypto::random::SecureRandom;
|
use zerotier_core_crypto::random::SecureRandom;
|
||||||
use zerotier_core_crypto::secret::Secret;
|
use zerotier_core_crypto::secret::Secret;
|
||||||
use zerotier_core_crypto::sidhp751::{SIDHPublicKeyAlice, SIDHPublicKeyBob, SIDHSecretKeyAlice, SIDHSecretKeyBob, SIDH_P751_PUBLIC_KEY_SIZE};
|
use zerotier_core_crypto::sidhp751::{SIDHPublicKeyAlice, SIDHPublicKeyBob, SIDHSecretKeyAlice, SIDHSecretKeyBob, SIDH_P751_PUBLIC_KEY_SIZE};
|
||||||
|
@ -23,44 +22,17 @@ use crate::vl1::Address;
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
use crate::vl1::symmetricsecret::SymmetricSecret;
|
use crate::vl1::symmetricsecret::SymmetricSecret;
|
||||||
|
|
||||||
const EPHEMERAL_PUBLIC_FLAG_HAVE_RATCHET_STATE_HMAC: u8 = 0x01;
|
|
||||||
|
|
||||||
pub const ALGORITHM_C25519: u8 = 0x01;
|
pub const ALGORITHM_C25519: u8 = 0x01;
|
||||||
pub const ALGORITHM_NISTP521ECDH: u8 = 0x02;
|
pub const ALGORITHM_NISTP384ECDH: u8 = 0x02;
|
||||||
pub const ALGORITHM_SIDHP751: u8 = 0x04;
|
pub const ALGORITHM_SIDHP751: u8 = 0x04;
|
||||||
|
|
||||||
pub enum EphemeralKeyAgreementError {
|
/// A set of ephemeral secret key pairs and related information.
|
||||||
OutdatedPublic,
|
|
||||||
StateMismatch,
|
|
||||||
InvalidData,
|
|
||||||
NoCompatibleAlgorithms
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for EphemeralKeyAgreementError {
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
match self {
|
|
||||||
EphemeralKeyAgreementError::OutdatedPublic => f.write_str("old (replayed?) public key data from remote"),
|
|
||||||
EphemeralKeyAgreementError::StateMismatch => f.write_str("ratchet state mismatch"),
|
|
||||||
EphemeralKeyAgreementError::InvalidData => f.write_str("invalid public key data"),
|
|
||||||
EphemeralKeyAgreementError::NoCompatibleAlgorithms => f.write_str("no compatible algorithms in public key data")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for EphemeralKeyAgreementError {
|
|
||||||
#[inline(always)]
|
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { <Self as Display>::fmt(self, f) }
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error for EphemeralKeyAgreementError {}
|
|
||||||
|
|
||||||
/// A set of ephemeral secret key pairs. Multiple algorithms are used.
|
|
||||||
pub(crate) struct EphemeralKeyPairSet {
|
pub(crate) struct EphemeralKeyPairSet {
|
||||||
previous_ratchet_count: u64, // Previous ratchet count, next ratchet should be this + 1
|
next_key: Secret<64>,
|
||||||
state_hmac: Option<[u8; 48]>, // HMAC of previous ratchet count, if there was a previous state
|
c25519: C25519KeyPair,
|
||||||
c25519: C25519KeyPair, // Hipster DJB cryptography
|
p384: P384KeyPair,
|
||||||
p521: P521KeyPair, // US Federal Government cryptography
|
sidhp751: Option<SIDHEphemeralKeyPair>,
|
||||||
sidhp751: Option<SIDHEphemeralKeyPair>, // Post-quantum moon math cryptography (not used in every ratchet tick)
|
previous_cumulative_algorithms: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EphemeralKeyPairSet {
|
impl EphemeralKeyPairSet {
|
||||||
|
@ -68,34 +40,24 @@ impl EphemeralKeyPairSet {
|
||||||
///
|
///
|
||||||
/// This contains key pairs for the asymmetric key agreement algorithms used and a
|
/// This contains key pairs for the asymmetric key agreement algorithms used and a
|
||||||
/// timestamp used to enforce TTL.
|
/// timestamp used to enforce TTL.
|
||||||
pub fn new(local_address: Address, remote_address: Address, previous_ephemeral_secret: Option<&EphemeralSymmetricSecret>) -> Self {
|
///
|
||||||
let (sidhp751, previous_ratchet_count, state_hmac) = previous_ephemeral_secret.map_or_else(|| {
|
/// The key and cumulative algorithms should come from the current in-effect ephemeral secret or
|
||||||
(
|
/// should be the long-term static secret and zero if there isn't one.
|
||||||
Some(SIDHEphemeralKeyPair::generate(local_address, remote_address)),
|
///
|
||||||
0,
|
/// SIDH is slow, so it's only included in the exchange if it's never been included in any previous
|
||||||
|
/// exchange. The threat model here is long-term data warehousing in anticipation of QC, so one SIDH
|
||||||
|
/// per ephemeral session is probably good enough for that.
|
||||||
|
pub fn new(local_address: Address, remote_address: Address, key: &SymmetricSecret, cumulative_algorithms: u8) -> Self {
|
||||||
|
Self {
|
||||||
|
next_key: zt_kbkdf_hmac_sha512(key.key.as_bytes(), KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_NEXT_KEY, 0, 0),
|
||||||
|
c25519: C25519KeyPair::generate(),
|
||||||
|
p384: P384KeyPair::generate(),
|
||||||
|
sidhp751: if (cumulative_algorithms & ALGORITHM_SIDHP751) == 0 {
|
||||||
|
Some(SIDHEphemeralKeyPair::generate(local_address, remote_address))
|
||||||
|
} else {
|
||||||
None
|
None
|
||||||
)
|
},
|
||||||
}, |previous_ephemeral_secret| {
|
previous_cumulative_algorithms: cumulative_algorithms
|
||||||
(
|
|
||||||
if (previous_ephemeral_secret.ratchet_count & 0xff) == 0 {
|
|
||||||
// We include SIDH every 256 ratchets, which for a 5 minute re-key interval
|
|
||||||
// means SIDH will be included about every 24 hours. SIDH is slower and is intended
|
|
||||||
// to guard against long term warehousing for eventual cracking with a QC, so this
|
|
||||||
// should be good enough for that threat model.
|
|
||||||
Some(SIDHEphemeralKeyPair::generate(local_address, remote_address))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
previous_ephemeral_secret.ratchet_count,
|
|
||||||
Some(SHA384::hmac(previous_ephemeral_secret.secret.ephemeral_ratchet_state_key.as_bytes(), &previous_ephemeral_secret.ratchet_count.to_be_bytes()))
|
|
||||||
)
|
|
||||||
});
|
|
||||||
EphemeralKeyPairSet {
|
|
||||||
previous_ratchet_count,
|
|
||||||
state_hmac,
|
|
||||||
c25519: C25519KeyPair::generate(true),
|
|
||||||
p521: P521KeyPair::generate(true).expect("NIST P-521 key pair generation failed"),
|
|
||||||
sidhp751,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -104,16 +66,7 @@ impl EphemeralKeyPairSet {
|
||||||
/// Note that the public key bundle is NOT self-signed or otherwise self-authenticating. It must
|
/// Note that the public key bundle is NOT self-signed or otherwise self-authenticating. It must
|
||||||
/// be transmitted over an authenticated channel.
|
/// be transmitted over an authenticated channel.
|
||||||
pub fn public_bytes(&self) -> Vec<u8> {
|
pub fn public_bytes(&self) -> Vec<u8> {
|
||||||
let mut b: Vec<u8> = Vec::with_capacity(SHA384_HASH_SIZE + 8 + C25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + SIDH_P751_PUBLIC_KEY_SIZE);
|
let mut b: Vec<u8> = Vec::with_capacity(C25519_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + SIDH_P751_PUBLIC_KEY_SIZE + 8);
|
||||||
|
|
||||||
let _ = varint::write(&mut b, self.previous_ratchet_count);
|
|
||||||
|
|
||||||
if let Some(state_hmac) = self.state_hmac.as_ref() {
|
|
||||||
b.push(EPHEMERAL_PUBLIC_FLAG_HAVE_RATCHET_STATE_HMAC);
|
|
||||||
let _ = b.write_all(state_hmac);
|
|
||||||
} else {
|
|
||||||
b.push(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
b.push(ALGORITHM_C25519);
|
b.push(ALGORITHM_C25519);
|
||||||
let _ = varint::write(&mut b, C25519_PUBLIC_KEY_SIZE as u64);
|
let _ = varint::write(&mut b, C25519_PUBLIC_KEY_SIZE as u64);
|
||||||
|
@ -135,103 +88,45 @@ impl EphemeralKeyPairSet {
|
||||||
// FIPS point of view. Final key must be HKDF(salt, a FIPS-compliant algorithm secret). There is zero
|
// FIPS point of view. Final key must be HKDF(salt, a FIPS-compliant algorithm secret). There is zero
|
||||||
// actual security implication to the order.
|
// actual security implication to the order.
|
||||||
|
|
||||||
b.push(ALGORITHM_NISTP521ECDH);
|
b.push(ALGORITHM_NISTP384ECDH);
|
||||||
let _ = varint::write(&mut b, P521_PUBLIC_KEY_SIZE as u64);
|
let _ = varint::write(&mut b, P384_PUBLIC_KEY_SIZE as u64);
|
||||||
let _ = b.write_all(self.p521.public_key_bytes());
|
let _ = b.write_all(self.p384.public_key_bytes());
|
||||||
|
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Perform ephemeral key agreement.
|
/// Perform ephemeral key agreement.
|
||||||
///
|
///
|
||||||
/// None is returned if the public key data is malformed, no algorithms overlap, etc.
|
|
||||||
///
|
|
||||||
/// Input is the previous session key. The long-lived identity key exchange key starts
|
|
||||||
/// the ratchet sequence, or rather a key derived from it for this purpose.
|
|
||||||
///
|
|
||||||
/// Since ephemeral secrets should only be used once, this consumes the object.
|
/// Since ephemeral secrets should only be used once, this consumes the object.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn agree(self, time_ticks: i64, static_secret: &SymmetricSecret, previous_ephemeral_secret: Option<&EphemeralSymmetricSecret>, other_public_bytes: &[u8]) -> Result<EphemeralSymmetricSecret, EphemeralKeyAgreementError> {
|
pub fn agree(self, time_ticks: i64, mut other_public_bytes: &[u8]) -> Option<EphemeralSymmetricSecret> {
|
||||||
let (mut key, mut ratchet_count, mut c25519_ratchet_count, mut sidhp751_ratchet_count, mut nistp521_ratchet_count) = previous_ephemeral_secret.map_or_else(|| {
|
let mut algorithms_successful: u8 = 0;
|
||||||
(
|
let mut fips_compliant_exchange = false;
|
||||||
static_secret.ephemeral_ratchet_key.clone(),
|
let mut key: Secret<64> = self.next_key.clone();
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
0
|
|
||||||
)
|
|
||||||
}, |previous_ephemeral_secret| {
|
|
||||||
(
|
|
||||||
previous_ephemeral_secret.secret.ephemeral_ratchet_key.clone(),
|
|
||||||
previous_ephemeral_secret.ratchet_count,
|
|
||||||
previous_ephemeral_secret.c25519_ratchet_count,
|
|
||||||
previous_ephemeral_secret.sidhp751_ratchet_count,
|
|
||||||
previous_ephemeral_secret.nistp521_ratchet_count
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut it_happened = false; // set to true if at least one exchange occurred
|
|
||||||
let mut fips_compliant_exchange = false; // is true in the end if the last algorithm was FIPS-compliant
|
|
||||||
let mut other_public_bytes = other_public_bytes;
|
|
||||||
|
|
||||||
// If the other side's ratchet counter is less than ours it means this may be a replayed
|
|
||||||
// public key (or duplicate packet) and should be ignored. If it's greater it's a state
|
|
||||||
// mismatch since there's no other way it could be from the future.
|
|
||||||
let other_ratchet_count = varint::read(&mut other_public_bytes);
|
|
||||||
if other_ratchet_count.is_err() {
|
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
|
||||||
}
|
|
||||||
let other_ratchet_count = other_ratchet_count.unwrap().0;
|
|
||||||
if other_ratchet_count < ratchet_count {
|
|
||||||
return Err(EphemeralKeyAgreementError::OutdatedPublic);
|
|
||||||
} else if other_ratchet_count > ratchet_count {
|
|
||||||
return Err(EphemeralKeyAgreementError::StateMismatch);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now check the other side's HMAC of the ratchet state to fully verify that the ratchet
|
|
||||||
// is aligned properly.
|
|
||||||
if other_public_bytes.is_empty() {
|
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
|
||||||
}
|
|
||||||
if (other_public_bytes[0] & EPHEMERAL_PUBLIC_FLAG_HAVE_RATCHET_STATE_HMAC) != 0 {
|
|
||||||
if other_public_bytes.len() < 49 {
|
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
|
||||||
}
|
|
||||||
if self.state_hmac.as_ref().map_or(true, |state_hmac| state_hmac != &other_public_bytes[1..49]) {
|
|
||||||
return Err(EphemeralKeyAgreementError::StateMismatch);
|
|
||||||
}
|
|
||||||
other_public_bytes = &other_public_bytes[49..];
|
|
||||||
} else {
|
|
||||||
if self.state_hmac.is_some() {
|
|
||||||
return Err(EphemeralKeyAgreementError::OutdatedPublic);
|
|
||||||
}
|
|
||||||
other_public_bytes = &other_public_bytes[1..];
|
|
||||||
}
|
|
||||||
|
|
||||||
while !other_public_bytes.is_empty() {
|
while !other_public_bytes.is_empty() {
|
||||||
let cipher = other_public_bytes[0];
|
let algorithms = other_public_bytes[0];
|
||||||
other_public_bytes = &other_public_bytes[1..];
|
other_public_bytes = &other_public_bytes[1..];
|
||||||
let key_len = varint::read(&mut other_public_bytes);
|
let key_len = varint::read(&mut other_public_bytes);
|
||||||
if key_len.is_err() {
|
if key_len.is_err() {
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
return None;
|
||||||
}
|
}
|
||||||
let key_len = key_len.unwrap().0 as usize;
|
let key_len = key_len.unwrap().0 as usize;
|
||||||
|
|
||||||
if cipher == ALGORITHM_C25519 {
|
if algorithms == ALGORITHM_C25519 {
|
||||||
if other_public_bytes.len() < C25519_PUBLIC_KEY_SIZE || key_len != C25519_PUBLIC_KEY_SIZE {
|
if other_public_bytes.len() < C25519_PUBLIC_KEY_SIZE || key_len != C25519_PUBLIC_KEY_SIZE {
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let c25519_secret = self.c25519.agree(&other_public_bytes[0..C25519_PUBLIC_KEY_SIZE]);
|
let c25519_secret = self.c25519.agree(&other_public_bytes[0..C25519_PUBLIC_KEY_SIZE]);
|
||||||
other_public_bytes = &other_public_bytes[C25519_PUBLIC_KEY_SIZE..];
|
other_public_bytes = &other_public_bytes[C25519_PUBLIC_KEY_SIZE..];
|
||||||
|
|
||||||
key.0 = SHA384::hmac(&key.0, &c25519_secret.0);
|
key.0 = hmac_sha512(&key.0, &c25519_secret.0);
|
||||||
it_happened = true;
|
algorithms_successful |= ALGORITHM_C25519;
|
||||||
fips_compliant_exchange = false;
|
fips_compliant_exchange = false;
|
||||||
c25519_ratchet_count += 1;
|
} else if algorithms == ALGORITHM_SIDHP751 {
|
||||||
} else if cipher == ALGORITHM_SIDHP751 {
|
|
||||||
if other_public_bytes.len() < (SIDH_P751_PUBLIC_KEY_SIZE + 1) || key_len != (SIDH_P751_PUBLIC_KEY_SIZE + 1) {
|
if other_public_bytes.len() < (SIDH_P751_PUBLIC_KEY_SIZE + 1) || key_len != (SIDH_P751_PUBLIC_KEY_SIZE + 1) {
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = match self.sidhp751.as_ref() {
|
let _ = match self.sidhp751.as_ref() {
|
||||||
|
@ -251,56 +146,48 @@ impl EphemeralKeyPairSet {
|
||||||
},
|
},
|
||||||
None => None,
|
None => None,
|
||||||
}.map(|sidh_secret| {
|
}.map(|sidh_secret| {
|
||||||
key.0 = SHA384::hmac(&key.0, &sidh_secret.0);
|
key.0 = hmac_sha512(&key.0, &sidh_secret.0);
|
||||||
it_happened = true;
|
algorithms_successful |= ALGORITHM_SIDHP751;
|
||||||
fips_compliant_exchange = false;
|
fips_compliant_exchange = false;
|
||||||
sidhp751_ratchet_count += 1;
|
|
||||||
});
|
});
|
||||||
other_public_bytes = &other_public_bytes[(SIDH_P751_PUBLIC_KEY_SIZE + 1)..];
|
other_public_bytes = &other_public_bytes[(SIDH_P751_PUBLIC_KEY_SIZE + 1)..];
|
||||||
} else if cipher == ALGORITHM_NISTP521ECDH {
|
} else if algorithms == ALGORITHM_NISTP384ECDH {
|
||||||
if other_public_bytes.len() < P521_PUBLIC_KEY_SIZE || key_len != P521_PUBLIC_KEY_SIZE {
|
if other_public_bytes.len() < P384_PUBLIC_KEY_SIZE || key_len != P384_PUBLIC_KEY_SIZE {
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let p521_public = P521PublicKey::from_bytes(&other_public_bytes[0..P521_PUBLIC_KEY_SIZE]);
|
let p384_public = P384PublicKey::from_bytes(&other_public_bytes[0..P384_PUBLIC_KEY_SIZE]);
|
||||||
other_public_bytes = &other_public_bytes[P521_PUBLIC_KEY_SIZE..];
|
other_public_bytes = &other_public_bytes[P384_PUBLIC_KEY_SIZE..];
|
||||||
if p521_public.is_none() {
|
if p384_public.is_none() {
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let p521_key = self.p521.agree(p521_public.as_ref().unwrap());
|
let _ = self.p384.agree(p384_public.as_ref().unwrap()).map(|p384_key| {
|
||||||
if p521_key.is_none() {
|
key.0 = hmac_sha512(&key.0, &p384_key.0);
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
algorithms_successful |= ALGORITHM_NISTP384ECDH;
|
||||||
}
|
fips_compliant_exchange = true;
|
||||||
|
});
|
||||||
key.0 = SHA384::hmac(&key.0, &p521_key.unwrap().0);
|
|
||||||
it_happened = true;
|
|
||||||
fips_compliant_exchange = true;
|
|
||||||
nistp521_ratchet_count += 1;
|
|
||||||
} else {
|
} else {
|
||||||
if other_public_bytes.len() < key_len {
|
if other_public_bytes.len() < key_len {
|
||||||
return Err(EphemeralKeyAgreementError::InvalidData);
|
return None;
|
||||||
}
|
}
|
||||||
other_public_bytes = &other_public_bytes[key_len..];
|
other_public_bytes = &other_public_bytes[key_len..];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return if it_happened {
|
return if algorithms_successful != 0 {
|
||||||
ratchet_count += 1;
|
Some(EphemeralSymmetricSecret {
|
||||||
Ok(EphemeralSymmetricSecret {
|
|
||||||
secret: SymmetricSecret::new(key),
|
secret: SymmetricSecret::new(key),
|
||||||
ratchet_count,
|
|
||||||
rekey_time: time_ticks + EPHEMERAL_SECRET_REKEY_AFTER_TIME,
|
rekey_time: time_ticks + EPHEMERAL_SECRET_REKEY_AFTER_TIME,
|
||||||
expire_time: time_ticks + EPHEMERAL_SECRET_REJECT_AFTER_TIME,
|
expire_time: time_ticks + EPHEMERAL_SECRET_REJECT_AFTER_TIME,
|
||||||
c25519_ratchet_count,
|
|
||||||
sidhp751_ratchet_count,
|
|
||||||
nistp521_ratchet_count,
|
|
||||||
encrypt_uses: AtomicU32::new(0),
|
encrypt_uses: AtomicU32::new(0),
|
||||||
decrypt_uses: AtomicU32::new(0),
|
decrypt_uses: AtomicU32::new(0),
|
||||||
|
algorithms: algorithms_successful,
|
||||||
|
cumulative_algorithms: algorithms_successful | self.previous_cumulative_algorithms,
|
||||||
fips_compliant_exchange
|
fips_compliant_exchange
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Err(EphemeralKeyAgreementError::NoCompatibleAlgorithms)
|
None
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,22 +196,18 @@ impl EphemeralKeyPairSet {
|
||||||
pub(crate) struct EphemeralSymmetricSecret {
|
pub(crate) struct EphemeralSymmetricSecret {
|
||||||
/// Current ephemeral secret key.
|
/// Current ephemeral secret key.
|
||||||
pub secret: SymmetricSecret,
|
pub secret: SymmetricSecret,
|
||||||
/// Total number of ratchets that has occurred.
|
|
||||||
pub ratchet_count: u64,
|
|
||||||
/// Time at or after which we should start trying to re-key.
|
/// Time at or after which we should start trying to re-key.
|
||||||
pub rekey_time: i64,
|
pub rekey_time: i64,
|
||||||
/// Time after which this key is no longer valid.
|
/// Time after which this key is no longer valid.
|
||||||
pub expire_time: i64,
|
pub expire_time: i64,
|
||||||
/// Number of C25519 agreements so far in ratchet.
|
|
||||||
pub c25519_ratchet_count: u64,
|
|
||||||
/// Number of SIDH P-751 agreements so far in ratchet.
|
|
||||||
pub sidhp751_ratchet_count: u64,
|
|
||||||
/// Number of NIST P-521 ECDH agreements so far in ratchet.
|
|
||||||
pub nistp521_ratchet_count: u64,
|
|
||||||
/// Number of times this secret has been used to encrypt.
|
/// Number of times this secret has been used to encrypt.
|
||||||
pub encrypt_uses: AtomicU32,
|
pub encrypt_uses: AtomicU32,
|
||||||
/// Number of times this secret has been used to decrypt.
|
/// Number of times this secret has been used to decrypt.
|
||||||
pub decrypt_uses: AtomicU32,
|
pub decrypt_uses: AtomicU32,
|
||||||
|
/// Algorithms used in this exchange (bit mask).
|
||||||
|
pub algorithms: u8,
|
||||||
|
/// Cumulative algorithm mask including previous exchange algorithms.
|
||||||
|
pub cumulative_algorithms: u8,
|
||||||
/// True if most recent key exchange was NIST/FIPS compliant.
|
/// True if most recent key exchange was NIST/FIPS compliant.
|
||||||
pub fips_compliant_exchange: bool,
|
pub fips_compliant_exchange: bool,
|
||||||
}
|
}
|
||||||
|
@ -388,39 +271,30 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ratchet() {
|
fn ratchet() {
|
||||||
let static_secret = SymmetricSecret::new(Secret([1_u8; 48]));
|
let static_secret = SymmetricSecret::new(Secret([1_u8; 64]));
|
||||||
let alice_address = Address::from_u64(0xdeadbeef00).unwrap();
|
let alice_address = Address::from_u64(0xdeadbeef00).unwrap();
|
||||||
let bob_address = Address::from_u64(0xbeefdead00).unwrap();
|
let bob_address = Address::from_u64(0xbeefdead00).unwrap();
|
||||||
let mut alice = EphemeralKeyPairSet::new(alice_address, bob_address, None);
|
let mut alice = EphemeralKeyPairSet::new(alice_address, bob_address, &static_secret, 0);
|
||||||
let mut bob = EphemeralKeyPairSet::new(bob_address, alice_address, None);
|
let mut bob = EphemeralKeyPairSet::new(bob_address, alice_address, &static_secret, 0);
|
||||||
let mut prev_alice_key = None;
|
|
||||||
let mut prev_bob_key = None;
|
|
||||||
let ratchets = 16;
|
let ratchets = 16;
|
||||||
|
let mut alice_cumulative_algorithms: u8 = 0;
|
||||||
|
let mut bob_cumulative_algorithms: u8 = 0;
|
||||||
for t in 1..ratchets+1 {
|
for t in 1..ratchets+1 {
|
||||||
let alice_public = alice.public_bytes();
|
let alice_public = alice.public_bytes();
|
||||||
let bob_public = bob.public_bytes();
|
let bob_public = bob.public_bytes();
|
||||||
let alice_key = alice.agree(t, &static_secret, prev_alice_key.as_ref(), bob_public.as_slice());
|
let alice_key = alice.agree(t, bob_public.as_slice());
|
||||||
let bob_key = bob.agree(t, &static_secret, prev_bob_key.as_ref(), alice_public.as_slice());
|
let bob_key = bob.agree(t, alice_public.as_slice());
|
||||||
assert!(alice_key.is_ok());
|
assert!(alice_key.is_some());
|
||||||
assert!(bob_key.is_ok());
|
assert!(bob_key.is_some());
|
||||||
let alice_key = alice_key.unwrap();
|
let alice_key = alice_key.unwrap();
|
||||||
let bob_key = bob_key.unwrap();
|
let bob_key = bob_key.unwrap();
|
||||||
|
alice_cumulative_algorithms |= alice_key.cumulative_algorithms;
|
||||||
|
bob_cumulative_algorithms |= bob_key.cumulative_algorithms;
|
||||||
assert_eq!(&alice_key.secret.key.0, &bob_key.secret.key.0);
|
assert_eq!(&alice_key.secret.key.0, &bob_key.secret.key.0);
|
||||||
//println!("alice: c25519={} p521={} sidh={} | bob: c25519={} p521={} sidh={}", alice_key.c25519_ratchet_count, alice_key.nistp521_ratchet_count, alice_key.sidhp751_ratchet_count, bob_key.c25519_ratchet_count, bob_key.nistp521_ratchet_count, bob_key.sidhp751_ratchet_count);
|
alice = EphemeralKeyPairSet::new(alice_address, bob_address, &alice_key.secret, alice_key.cumulative_algorithms);
|
||||||
alice = EphemeralKeyPairSet::new(alice_address, bob_address, Some(&alice_key));
|
bob = EphemeralKeyPairSet::new(bob_address, alice_address, &alice_key.secret, alice_key.cumulative_algorithms);
|
||||||
bob = EphemeralKeyPairSet::new(bob_address, alice_address, Some(&bob_key));
|
|
||||||
prev_alice_key = Some(alice_key);
|
|
||||||
prev_bob_key = Some(bob_key);
|
|
||||||
}
|
}
|
||||||
let last_alice_key = prev_alice_key.unwrap();
|
assert_ne!(alice_cumulative_algorithms, 0);
|
||||||
let last_bob_key = prev_bob_key.unwrap();
|
assert_ne!(bob_cumulative_algorithms, 0);
|
||||||
assert_eq!(last_alice_key.ratchet_count, ratchets as u64);
|
|
||||||
assert_eq!(last_bob_key.ratchet_count, ratchets as u64);
|
|
||||||
assert_eq!(last_alice_key.c25519_ratchet_count, ratchets as u64);
|
|
||||||
assert_eq!(last_bob_key.c25519_ratchet_count, ratchets as u64);
|
|
||||||
assert_eq!(last_alice_key.nistp521_ratchet_count, ratchets as u64);
|
|
||||||
assert_eq!(last_bob_key.nistp521_ratchet_count, ratchets as u64);
|
|
||||||
assert_eq!(last_alice_key.sidhp751_ratchet_count, last_bob_key.sidhp751_ratchet_count);
|
|
||||||
assert!(last_alice_key.sidhp751_ratchet_count >= 1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,19 +15,16 @@ use std::mem::MaybeUninit;
|
||||||
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
|
use std::ptr::{slice_from_raw_parts, slice_from_raw_parts_mut};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use highway::HighwayHash;
|
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
use zerotier_core_crypto::c25519::{C25519_PUBLIC_KEY_SIZE, C25519_SECRET_KEY_SIZE, C25519KeyPair, ED25519_PUBLIC_KEY_SIZE, ED25519_SECRET_KEY_SIZE, ED25519_SIGNATURE_SIZE, ed25519_verify, Ed25519KeyPair};
|
use zerotier_core_crypto::c25519::*;
|
||||||
use zerotier_core_crypto::hash::{SHA384, SHA384_HASH_SIZE, SHA512};
|
use zerotier_core_crypto::hash::{hmac_sha512, SHA384, SHA384_HASH_SIZE, SHA512, SHA512_HASH_SIZE};
|
||||||
use zerotier_core_crypto::hex;
|
use zerotier_core_crypto::hex;
|
||||||
use zerotier_core_crypto::p521::{P521_ECDSA_SIGNATURE_SIZE, P521_PUBLIC_KEY_SIZE, P521_SECRET_KEY_SIZE, P521KeyPair, P521PublicKey};
|
use zerotier_core_crypto::p384::*;
|
||||||
use zerotier_core_crypto::salsa::Salsa;
|
use zerotier_core_crypto::salsa::Salsa;
|
||||||
use zerotier_core_crypto::secret::Secret;
|
use zerotier_core_crypto::secret::Secret;
|
||||||
|
|
||||||
use crate::error::InvalidFormatError;
|
use crate::error::InvalidFormatError;
|
||||||
use crate::util::{array_range, highwayhasher, u128_from_2xu64_ne};
|
|
||||||
use crate::util::buffer::Buffer;
|
use crate::util::buffer::Buffer;
|
||||||
use crate::util::pool::{Pool, Pooled, PoolFactory};
|
use crate::util::pool::{Pool, Pooled, PoolFactory};
|
||||||
use crate::vl1::Address;
|
use crate::vl1::Address;
|
||||||
|
@ -36,26 +33,26 @@ use crate::vl1::protocol::{ADDRESS_SIZE, ADDRESS_SIZE_STRING, IDENTITY_V0_POW_TH
|
||||||
/// Curve25519 and Ed25519
|
/// Curve25519 and Ed25519
|
||||||
pub const IDENTITY_ALGORITHM_X25519: u8 = 0x01;
|
pub const IDENTITY_ALGORITHM_X25519: u8 = 0x01;
|
||||||
|
|
||||||
/// NIST P-521 ECDH and ECDSA
|
/// NIST P-384 ECDH and ECDSA
|
||||||
pub const IDENTITY_ALGORITHM_EC_NIST_P521: u8 = 0x02;
|
pub const IDENTITY_ALGORITHM_EC_NIST_P384: u8 = 0x02;
|
||||||
|
|
||||||
/// Bit mask to include all algorithms.
|
/// Bit mask to include all algorithms.
|
||||||
pub const IDENTITY_ALGORITHM_ALL: u8 = 0xff;
|
pub const IDENTITY_ALGORITHM_ALL: u8 = 0xff;
|
||||||
|
|
||||||
/// Current sanity limit for the size of a marshaled Identity (can be increased if needed).
|
/// Current sanity limit for the size of a marshaled Identity (can be increased if needed).
|
||||||
pub const MAX_MARSHAL_SIZE: usize = ADDRESS_SIZE + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + 16;
|
pub const MAX_MARSHAL_SIZE: usize = ADDRESS_SIZE + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + C25519_SECRET_KEY_SIZE + ED25519_SECRET_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + 16;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IdentityP521Secret {
|
pub struct IdentityP384Secret {
|
||||||
pub ecdh: P521KeyPair,
|
pub ecdh: P384KeyPair,
|
||||||
pub ecdsa: P521KeyPair,
|
pub ecdsa: P384KeyPair,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct IdentityP521Public {
|
pub struct IdentityP384Public {
|
||||||
pub ecdh: [u8; P521_PUBLIC_KEY_SIZE],
|
pub ecdh: P384PublicKey,
|
||||||
pub ecdsa: [u8; P521_PUBLIC_KEY_SIZE],
|
pub ecdsa: P384PublicKey,
|
||||||
pub ecdsa_self_signature: [u8; P521_ECDSA_SIGNATURE_SIZE],
|
pub ecdsa_self_signature: [u8; P384_ECDSA_SIGNATURE_SIZE],
|
||||||
pub ed25519_self_signature: [u8; ED25519_SIGNATURE_SIZE],
|
pub ed25519_self_signature: [u8; ED25519_SIGNATURE_SIZE],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,22 +60,22 @@ pub struct IdentityP521Public {
|
||||||
pub struct IdentitySecret {
|
pub struct IdentitySecret {
|
||||||
pub c25519: C25519KeyPair,
|
pub c25519: C25519KeyPair,
|
||||||
pub ed25519: Ed25519KeyPair,
|
pub ed25519: Ed25519KeyPair,
|
||||||
pub p521: Option<IdentityP521Secret>,
|
pub p384: Option<IdentityP384Secret>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Identity {
|
pub struct Identity {
|
||||||
pub address: Address,
|
pub address: Address,
|
||||||
fast_eq_hash: u128, // highwayhash used internally for very fast eq()
|
|
||||||
pub c25519: [u8; C25519_PUBLIC_KEY_SIZE],
|
pub c25519: [u8; C25519_PUBLIC_KEY_SIZE],
|
||||||
pub ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
|
pub ed25519: [u8; ED25519_PUBLIC_KEY_SIZE],
|
||||||
pub p521: Option<IdentityP521Public>,
|
pub p384: Option<IdentityP384Public>,
|
||||||
pub secret: Option<IdentitySecret>,
|
pub secret: Option<IdentitySecret>,
|
||||||
|
pub fingerprint: [u8; SHA512_HASH_SIZE]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn concat_arrays_2<const A: usize, const B: usize, const S: usize>(a: &[u8; A], b: &[u8; B]) -> [u8; S] {
|
fn concat_arrays_2<const A: usize, const B: usize, const S: usize>(a: &[u8; A], b: &[u8; B]) -> [u8; S] {
|
||||||
debug_assert_eq!(A + B, S);
|
assert_eq!(A + B, S);
|
||||||
let mut tmp: [u8; S] = unsafe { MaybeUninit::uninit().assume_init() };
|
let mut tmp: [u8; S] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
tmp[..A].copy_from_slice(a);
|
tmp[..A].copy_from_slice(a);
|
||||||
tmp[A..].copy_from_slice(b);
|
tmp[A..].copy_from_slice(b);
|
||||||
|
@ -87,7 +84,7 @@ fn concat_arrays_2<const A: usize, const B: usize, const S: usize>(a: &[u8; A],
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn concat_arrays_4<const A: usize, const B: usize, const C: usize, const D: usize, const S: usize>(a: &[u8; A], b: &[u8; B], c: &[u8; C], d: &[u8; D]) -> [u8; S] {
|
fn concat_arrays_4<const A: usize, const B: usize, const C: usize, const D: usize, const S: usize>(a: &[u8; A], b: &[u8; B], c: &[u8; C], d: &[u8; D]) -> [u8; S] {
|
||||||
debug_assert_eq!(A + B + C + D, S);
|
assert_eq!(A + B + C + D, S);
|
||||||
let mut tmp: [u8; S] = unsafe { MaybeUninit::uninit().assume_init() };
|
let mut tmp: [u8; S] = unsafe { MaybeUninit::uninit().assume_init() };
|
||||||
tmp[..A].copy_from_slice(a);
|
tmp[..A].copy_from_slice(a);
|
||||||
tmp[A..(A + B)].copy_from_slice(b);
|
tmp[A..(A + B)].copy_from_slice(b);
|
||||||
|
@ -100,14 +97,14 @@ impl Identity {
|
||||||
/// Generate a new identity.
|
/// Generate a new identity.
|
||||||
pub fn generate() -> Self {
|
pub fn generate() -> Self {
|
||||||
let mut sha = SHA512::new();
|
let mut sha = SHA512::new();
|
||||||
let ed25519 = Ed25519KeyPair::generate(false);
|
let ed25519 = Ed25519KeyPair::generate();
|
||||||
let ed25519_pub = ed25519.public_bytes();
|
let ed25519_pub = ed25519.public_bytes();
|
||||||
let address;
|
let address;
|
||||||
let mut c25519;
|
let mut c25519;
|
||||||
let mut c25519_pub;
|
let mut c25519_pub;
|
||||||
let mut genmem_pool_obj = ADDRESS_DERVIATION_MEMORY_POOL.get();
|
let mut genmem_pool_obj = ADDRESS_DERVIATION_MEMORY_POOL.get();
|
||||||
loop {
|
loop {
|
||||||
c25519 = C25519KeyPair::generate(false);
|
c25519 = C25519KeyPair::generate();
|
||||||
c25519_pub = c25519.public_bytes();
|
c25519_pub = c25519.public_bytes();
|
||||||
|
|
||||||
sha.update(&c25519_pub);
|
sha.update(&c25519_pub);
|
||||||
|
@ -127,72 +124,69 @@ impl Identity {
|
||||||
}
|
}
|
||||||
drop(genmem_pool_obj);
|
drop(genmem_pool_obj);
|
||||||
|
|
||||||
let p521_ecdh = P521KeyPair::generate(false).unwrap();
|
let p384_ecdh = P384KeyPair::generate();
|
||||||
let p521_ecdsa = P521KeyPair::generate(false).unwrap();
|
let p384_ecdsa = P384KeyPair::generate();
|
||||||
let p521_ecdh_pub = p521_ecdh.public_key_bytes().clone();
|
|
||||||
let p521_ecdsa_pub = p521_ecdsa.public_key_bytes().clone();
|
|
||||||
|
|
||||||
let mut self_sign_buf: Vec<u8> = Vec::with_capacity(ADDRESS_SIZE + 4 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE);
|
let mut self_sign_buf: Vec<u8> = Vec::with_capacity(ADDRESS_SIZE + 4 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE);
|
||||||
let _ = self_sign_buf.write_all(&address.to_bytes());
|
let _ = self_sign_buf.write_all(&address.to_bytes());
|
||||||
let _ = self_sign_buf.write_all(&c25519_pub);
|
let _ = self_sign_buf.write_all(&c25519_pub);
|
||||||
let _ = self_sign_buf.write_all(&ed25519_pub);
|
let _ = self_sign_buf.write_all(&ed25519_pub);
|
||||||
self_sign_buf.push(IDENTITY_ALGORITHM_EC_NIST_P521);
|
self_sign_buf.push(IDENTITY_ALGORITHM_EC_NIST_P384);
|
||||||
let _ = self_sign_buf.write_all(&p521_ecdh_pub);
|
let _ = self_sign_buf.write_all(p384_ecdh.public_key_bytes());
|
||||||
let _ = self_sign_buf.write_all(&p521_ecdsa_pub);
|
let _ = self_sign_buf.write_all(p384_ecdsa.public_key_bytes());
|
||||||
self_sign_buf.push(IDENTITY_ALGORITHM_EC_NIST_P521);
|
|
||||||
|
|
||||||
let mut hh = highwayhasher();
|
let ecdsa_self_signature = p384_ecdsa.sign(self_sign_buf.as_slice());
|
||||||
Hasher::write_u64(&mut hh, address.to_u64());
|
let ed25519_self_signature = ed25519.sign(self_sign_buf.as_slice());
|
||||||
Hasher::write(&mut hh, &c25519_pub);
|
|
||||||
Hasher::write(&mut hh, &ed25519_pub);
|
let mut sha = SHA512::new();
|
||||||
Hasher::write(&mut hh, &p521_ecdh_pub);
|
sha.update(self_sign_buf.as_slice());
|
||||||
Hasher::write(&mut hh, &p521_ecdsa_pub);
|
sha.update(&ecdsa_self_signature);
|
||||||
|
sha.update(&ed25519_self_signature);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
address,
|
address,
|
||||||
fast_eq_hash: u128_from_2xu64_ne(hh.finalize128()),
|
|
||||||
c25519: c25519_pub,
|
c25519: c25519_pub,
|
||||||
ed25519: ed25519_pub,
|
ed25519: ed25519_pub,
|
||||||
p521: Some(IdentityP521Public {
|
p384: Some(IdentityP384Public {
|
||||||
ecdh: p521_ecdh_pub,
|
ecdh: p384_ecdh.public_key().clone(),
|
||||||
ecdsa: p521_ecdsa_pub,
|
ecdsa: p384_ecdsa.public_key().clone(),
|
||||||
ecdsa_self_signature: p521_ecdsa.sign(self_sign_buf.as_slice()).expect("NIST P-521 signature failed in identity generation"),
|
ecdsa_self_signature,
|
||||||
ed25519_self_signature: ed25519.sign(self_sign_buf.as_slice()),
|
ed25519_self_signature,
|
||||||
}),
|
}),
|
||||||
secret: Some(IdentitySecret {
|
secret: Some(IdentitySecret {
|
||||||
c25519,
|
c25519,
|
||||||
ed25519,
|
ed25519,
|
||||||
p521: Some(IdentityP521Secret {
|
p384: Some(IdentityP384Secret {
|
||||||
ecdh: p521_ecdh,
|
ecdh: p384_ecdh,
|
||||||
ecdsa: p521_ecdsa,
|
ecdsa: p384_ecdsa,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
|
fingerprint: sha.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn algorithms(&self) -> u8 {
|
pub fn algorithms(&self) -> u8 {
|
||||||
if self.p521.is_some() {
|
if self.p384.is_some() {
|
||||||
IDENTITY_ALGORITHM_X25519 | IDENTITY_ALGORITHM_EC_NIST_P521
|
IDENTITY_ALGORITHM_X25519 | IDENTITY_ALGORITHM_EC_NIST_P384
|
||||||
} else {
|
} else {
|
||||||
IDENTITY_ALGORITHM_X25519
|
IDENTITY_ALGORITHM_X25519
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get a SHA384 hash of this identity's address and public keys.
|
/// Get a SHA384 hash of this identity's address and public keys.
|
||||||
/// This provides a globally unique 384-bit fingerprint of this identity.
|
/// This is a SHA384 counterpart to the sha512 field.
|
||||||
pub fn hash(&self) -> [u8; SHA384_HASH_SIZE] {
|
pub fn sha384(&self) -> [u8; SHA384_HASH_SIZE] {
|
||||||
let mut sha = SHA384::new();
|
let mut sha = SHA384::new();
|
||||||
sha.update(&self.address.to_bytes());
|
sha.update(&self.address.to_bytes());
|
||||||
sha.update(&self.c25519);
|
sha.update(&self.c25519);
|
||||||
sha.update(&self.ed25519);
|
sha.update(&self.ed25519);
|
||||||
let _ = self.p521.as_ref().map(|p521| {
|
let _ = self.p384.as_ref().map(|p384| {
|
||||||
sha.update(&[IDENTITY_ALGORITHM_EC_NIST_P521]);
|
sha.update(&[IDENTITY_ALGORITHM_EC_NIST_P384]);
|
||||||
sha.update(&p521.ecdh);
|
sha.update(p384.ecdh.as_bytes());
|
||||||
sha.update(&p521.ecdsa);
|
sha.update(p384.ecdsa.as_bytes());
|
||||||
sha.update(&p521.ecdsa_self_signature);
|
sha.update(&p384.ecdsa_self_signature);
|
||||||
sha.update(&p521.ed25519_self_signature);
|
sha.update(&p384.ed25519_self_signature);
|
||||||
sha.update(&[IDENTITY_ALGORITHM_EC_NIST_P521]);
|
|
||||||
});
|
});
|
||||||
sha.finish()
|
sha.finish()
|
||||||
}
|
}
|
||||||
|
@ -201,22 +195,21 @@ impl Identity {
|
||||||
///
|
///
|
||||||
/// This is somewhat time consuming due to the memory-intensive work algorithm.
|
/// This is somewhat time consuming due to the memory-intensive work algorithm.
|
||||||
pub fn validate_identity(&self) -> bool {
|
pub fn validate_identity(&self) -> bool {
|
||||||
let pow_threshold = if self.p521.is_some() {
|
let pow_threshold = if self.p384.is_some() {
|
||||||
let p521 = self.p521.as_ref().unwrap();
|
let p384 = self.p384.as_ref().unwrap();
|
||||||
|
|
||||||
let mut self_sign_buf: Vec<u8> = Vec::with_capacity(ADDRESS_SIZE + 4 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE);
|
let mut self_sign_buf: Vec<u8> = Vec::with_capacity(ADDRESS_SIZE + 4 + C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE);
|
||||||
let _ = self_sign_buf.write_all(&self.address.to_bytes());
|
let _ = self_sign_buf.write_all(&self.address.to_bytes());
|
||||||
let _ = self_sign_buf.write_all(&self.c25519);
|
let _ = self_sign_buf.write_all(&self.c25519);
|
||||||
let _ = self_sign_buf.write_all(&self.ed25519);
|
let _ = self_sign_buf.write_all(&self.ed25519);
|
||||||
self_sign_buf.push(IDENTITY_ALGORITHM_EC_NIST_P521);
|
self_sign_buf.push(IDENTITY_ALGORITHM_EC_NIST_P384);
|
||||||
let _ = self_sign_buf.write_all(&p521.ecdh);
|
let _ = self_sign_buf.write_all(p384.ecdh.as_bytes());
|
||||||
let _ = self_sign_buf.write_all(&p521.ecdsa);
|
let _ = self_sign_buf.write_all(p384.ecdsa.as_bytes());
|
||||||
self_sign_buf.push(IDENTITY_ALGORITHM_EC_NIST_P521);
|
|
||||||
|
|
||||||
if !P521PublicKey::from_bytes(&p521.ecdsa).map_or(false, |ecdsa_pub| ecdsa_pub.verify(self_sign_buf.as_slice(), &p521.ecdsa_self_signature)) {
|
if !p384.ecdsa.verify(self_sign_buf.as_slice(), &p384.ecdsa_self_signature) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if !ed25519_verify(&self.ed25519, &p521.ed25519_self_signature, self_sign_buf.as_slice()) {
|
if !ed25519_verify(&self.ed25519, &p384.ed25519_self_signature, self_sign_buf.as_slice()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,49 +233,51 @@ impl Identity {
|
||||||
///
|
///
|
||||||
/// An error can occur if this identity does not hold its secret portion or if either key is invalid.
|
/// An error can occur if this identity does not hold its secret portion or if either key is invalid.
|
||||||
///
|
///
|
||||||
/// If both sides have NIST P-521 keys then key agreement is performed using both Curve25519 and
|
/// If both sides have NIST P-384 keys then key agreement is performed using both Curve25519 and
|
||||||
/// NIST P-521 and the result is HMAC(Curve25519 secret, NIST P-521 secret).
|
/// NIST P-384 and the result is HMAC(Curve25519 secret, NIST P-384 secret).
|
||||||
pub fn agree(&self, other: &Identity) -> Option<Secret<48>> {
|
///
|
||||||
|
/// Nothing actually uses a 512-bit secret directly, but if the base secret is 512 bits then
|
||||||
|
/// no entropy is lost when deriving secrets with a KDF. Ciphers like AES use the first 256 bits
|
||||||
|
/// of these keys.
|
||||||
|
pub fn agree(&self, other: &Identity) -> Option<Secret<64>> {
|
||||||
self.secret.as_ref().and_then(|secret| {
|
self.secret.as_ref().and_then(|secret| {
|
||||||
let c25519_secret = Secret(SHA512::hash(&secret.c25519.agree(&other.c25519).0));
|
let c25519_secret = Secret(SHA512::hash(&secret.c25519.agree(&other.c25519).0));
|
||||||
|
|
||||||
// FIPS note: FIPS-compliant exchange algorithms must be the last algorithms in any HKDF chain
|
// FIPS note: FIPS-compliant exchange algorithms must be the last algorithms in any HKDF chain
|
||||||
// for the final result to be technically FIPS compliant. Non-FIPS algorithm secrets are considered
|
// for the final result to be technically FIPS compliant. Non-FIPS algorithm secrets are considered
|
||||||
// a salt in the HMAC(salt, key) HKDF construction.
|
// a salt in the HMAC(salt, key) HKDF construction.
|
||||||
if secret.p521.is_some() && other.p521.is_some() {
|
if secret.p384.is_some() && other.p384.is_some() {
|
||||||
P521PublicKey::from_bytes(&other.p521.as_ref().unwrap().ecdh).and_then(|other_p521| {
|
secret.p384.as_ref().unwrap().ecdh.agree(&other.p384.as_ref().unwrap().ecdh).map(|p384_secret| {
|
||||||
secret.p521.as_ref().unwrap().ecdh.agree(&other_p521).map(|p521_secret| {
|
Secret(hmac_sha512(&c25519_secret.0, &p384_secret.0))
|
||||||
Secret(SHA384::hmac(&c25519_secret.0[0..48], &p521_secret.0))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
Some(Secret(array_range::<u8, 64, 0, 48>(&c25519_secret.0).clone()))
|
Some(c25519_secret)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sign a message with this identity.
|
/// Sign a message with this identity.
|
||||||
///
|
///
|
||||||
|
/// If legacy_compatibility is true this generates only an ed25519 signature. Otherwise it
|
||||||
|
/// will generate a signature using both the ed25519 key and the P-384 key if the latter
|
||||||
|
/// is present in the identity.
|
||||||
|
///
|
||||||
/// A return of None happens if we don't have our secret key(s) or some other error occurs.
|
/// A return of None happens if we don't have our secret key(s) or some other error occurs.
|
||||||
pub fn sign(&self, msg: &[u8], legacy_compatibility: bool) -> Option<Vec<u8>> {
|
pub fn sign(&self, msg: &[u8], legacy_compatibility: bool) -> Option<Vec<u8>> {
|
||||||
if self.secret.is_some() {
|
if self.secret.is_some() {
|
||||||
let secret = self.secret.as_ref().unwrap();
|
let secret = self.secret.as_ref().unwrap();
|
||||||
if legacy_compatibility {
|
if legacy_compatibility {
|
||||||
Some(secret.ed25519.sign_zt(msg).to_vec())
|
Some(secret.ed25519.sign_zt(msg).to_vec())
|
||||||
} else if secret.p521.is_some() {
|
} else if secret.p384.is_some() {
|
||||||
let p521 = secret.p521.as_ref().unwrap();
|
let mut tmp: Vec<u8> = Vec::with_capacity(1 + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE);
|
||||||
let p521_sig = p521.ecdsa.sign(msg).unwrap();
|
tmp.push(IDENTITY_ALGORITHM_X25519 | IDENTITY_ALGORITHM_EC_NIST_P384);
|
||||||
let ed25519_sig = secret.ed25519.sign(msg);
|
let _ = tmp.write_all(&secret.p384.as_ref().unwrap().ecdsa.sign(msg));
|
||||||
let mut tmp: Vec<u8> = Vec::with_capacity(1 + P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE);
|
let _ = tmp.write_all(&secret.ed25519.sign(msg));
|
||||||
tmp.push(IDENTITY_ALGORITHM_X25519 | IDENTITY_ALGORITHM_EC_NIST_P521);
|
|
||||||
let _ = tmp.write_all(&ed25519_sig);
|
|
||||||
let _ = tmp.write_all(&p521_sig);
|
|
||||||
Some(tmp)
|
Some(tmp)
|
||||||
} else {
|
} else {
|
||||||
let ed25519_sig = secret.ed25519.sign(msg);
|
|
||||||
let mut tmp: Vec<u8> = Vec::with_capacity(1 + ED25519_SIGNATURE_SIZE);
|
let mut tmp: Vec<u8> = Vec::with_capacity(1 + ED25519_SIGNATURE_SIZE);
|
||||||
tmp.push(IDENTITY_ALGORITHM_X25519);
|
tmp.push(IDENTITY_ALGORITHM_X25519);
|
||||||
let _ = tmp.write_all(&ed25519_sig);
|
let _ = tmp.write_all(&secret.ed25519.sign(msg));
|
||||||
Some(tmp)
|
Some(tmp)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -299,16 +294,16 @@ impl Identity {
|
||||||
signature = &signature[1..];
|
signature = &signature[1..];
|
||||||
let mut ok = true;
|
let mut ok = true;
|
||||||
let mut checked = false;
|
let mut checked = false;
|
||||||
|
if ok && (algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0 && signature.len() >= P384_ECDSA_SIGNATURE_SIZE && self.p384.is_some() {
|
||||||
|
ok = self.p384.as_ref().unwrap().ecdsa.verify(msg, &signature[..P384_ECDSA_SIGNATURE_SIZE]);
|
||||||
|
signature = &signature[P384_ECDSA_SIGNATURE_SIZE..];
|
||||||
|
checked = true;
|
||||||
|
}
|
||||||
if ok && (algorithms & IDENTITY_ALGORITHM_X25519) != 0 && signature.len() >= ED25519_SIGNATURE_SIZE {
|
if ok && (algorithms & IDENTITY_ALGORITHM_X25519) != 0 && signature.len() >= ED25519_SIGNATURE_SIZE {
|
||||||
ok = ed25519_verify(&self.ed25519, &signature[..ED25519_SIGNATURE_SIZE], msg);
|
ok = ed25519_verify(&self.ed25519, &signature[..ED25519_SIGNATURE_SIZE], msg);
|
||||||
signature = &signature[ED25519_SIGNATURE_SIZE..];
|
signature = &signature[ED25519_SIGNATURE_SIZE..];
|
||||||
checked = true;
|
checked = true;
|
||||||
}
|
}
|
||||||
if ok && (algorithms & IDENTITY_ALGORITHM_EC_NIST_P521) != 0 && signature.len() >= P521_ECDSA_SIGNATURE_SIZE && self.p521.is_some() {
|
|
||||||
ok = P521PublicKey::from_bytes(&self.p521.as_ref().unwrap().ecdsa).map_or(false, |p521| p521.verify(msg, &signature[..P521_ECDSA_SIGNATURE_SIZE]));
|
|
||||||
signature = &signature[P521_ECDSA_SIGNATURE_SIZE..];
|
|
||||||
checked = true;
|
|
||||||
}
|
|
||||||
checked && ok
|
checked && ok
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -322,15 +317,15 @@ impl Identity {
|
||||||
b
|
b
|
||||||
}
|
}
|
||||||
|
|
||||||
const P521_PUBLIC_AND_PRIVATE_BUNDLE_SIZE: u16 = (P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE) as u16;
|
const P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE: u16 = (P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE + P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE) as u16;
|
||||||
const P521_PUBLIC_ONLY_BUNDLE_SIZE: u16 = (P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE) as u16;
|
const P384_PUBLIC_ONLY_BUNDLE_SIZE: u16 = (P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE) as u16;
|
||||||
|
|
||||||
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>, include_algorithms: u8, include_private: bool) -> std::io::Result<()> {
|
pub fn marshal<const BL: usize>(&self, buf: &mut Buffer<BL>, include_algorithms: u8, include_private: bool) -> std::io::Result<()> {
|
||||||
let algorithms = self.algorithms() & include_algorithms;
|
let algorithms = self.algorithms() & include_algorithms;
|
||||||
let secret = self.secret.as_ref();
|
let secret = self.secret.as_ref();
|
||||||
|
|
||||||
buf.append_bytes_fixed(&self.address.to_bytes())?;
|
buf.append_bytes_fixed(&self.address.to_bytes())?;
|
||||||
buf.append_u8(0x00)?; // use 0x00 here for backward compatibility
|
buf.append_u8(0x00)?; // LEGACY: 0x00 here for backward compatibility
|
||||||
buf.append_bytes_fixed(&self.c25519)?;
|
buf.append_bytes_fixed(&self.c25519)?;
|
||||||
buf.append_bytes_fixed(&self.ed25519)?;
|
buf.append_bytes_fixed(&self.ed25519)?;
|
||||||
if include_private && secret.is_some() {
|
if include_private && secret.is_some() {
|
||||||
|
@ -342,8 +337,8 @@ impl Identity {
|
||||||
buf.append_u8(0)?;
|
buf.append_u8(0)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (algorithms & IDENTITY_ALGORITHM_EC_NIST_P521) == IDENTITY_ALGORITHM_EC_NIST_P521 && self.p521.is_some() {
|
if (algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) != 0 && self.p384.is_some() {
|
||||||
let p521 = self.p521.as_ref().unwrap();
|
let p384 = self.p384.as_ref().unwrap();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For legacy backward compatibility, any key pairs and other material after the x25519
|
* For legacy backward compatibility, any key pairs and other material after the x25519
|
||||||
|
@ -361,28 +356,28 @@ impl Identity {
|
||||||
* Key agreement can then proceed using only x25519 keys.
|
* Key agreement can then proceed using only x25519 keys.
|
||||||
*/
|
*/
|
||||||
buf.append_u8(0x03)?;
|
buf.append_u8(0x03)?;
|
||||||
let p521_has_private = if include_private && secret.map_or(false, |s| s.p521.is_some()) {
|
let p384_has_private = if include_private && secret.map_or(false, |s| s.p384.is_some()) {
|
||||||
buf.append_u16(Self::P521_PUBLIC_AND_PRIVATE_BUNDLE_SIZE + 1 + 2)?;
|
buf.append_u16(Self::P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE + 1 + 2)?;
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
buf.append_u16(Self::P521_PUBLIC_ONLY_BUNDLE_SIZE + 1 + 2)?;
|
buf.append_u16(Self::P384_PUBLIC_ONLY_BUNDLE_SIZE + 1 + 2)?;
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
buf.append_u8(IDENTITY_ALGORITHM_EC_NIST_P521)?;
|
buf.append_u8(IDENTITY_ALGORITHM_EC_NIST_P384)?;
|
||||||
if p521_has_private {
|
if p384_has_private {
|
||||||
buf.append_u16(Self::P521_PUBLIC_AND_PRIVATE_BUNDLE_SIZE)?;
|
buf.append_u16(Self::P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE)?;
|
||||||
} else {
|
} else {
|
||||||
buf.append_u16(Self::P521_PUBLIC_ONLY_BUNDLE_SIZE)?;
|
buf.append_u16(Self::P384_PUBLIC_ONLY_BUNDLE_SIZE)?;
|
||||||
}
|
}
|
||||||
buf.append_bytes_fixed(&p521.ecdh)?;
|
buf.append_bytes_fixed(p384.ecdh.as_bytes())?;
|
||||||
buf.append_bytes_fixed(&p521.ecdsa)?;
|
buf.append_bytes_fixed(p384.ecdsa.as_bytes())?;
|
||||||
buf.append_bytes_fixed(&p521.ecdsa_self_signature)?;
|
buf.append_bytes_fixed(&p384.ecdsa_self_signature)?;
|
||||||
buf.append_bytes_fixed(&p521.ed25519_self_signature)?;
|
buf.append_bytes_fixed(&p384.ed25519_self_signature)?;
|
||||||
if p521_has_private {
|
if p384_has_private {
|
||||||
let p521s = secret.unwrap().p521.as_ref().unwrap();
|
let p384s = secret.unwrap().p384.as_ref().unwrap();
|
||||||
buf.append_bytes_fixed(&p521s.ecdh.secret_key_bytes().0)?;
|
buf.append_bytes_fixed(&p384s.ecdh.secret_key_bytes().0)?;
|
||||||
buf.append_bytes_fixed(&p521s.ecdsa.secret_key_bytes().0)?;
|
buf.append_bytes_fixed(&p384s.ecdsa.secret_key_bytes().0)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -398,8 +393,8 @@ impl Identity {
|
||||||
|
|
||||||
let mut x25519_public: Option<([u8; C25519_PUBLIC_KEY_SIZE], [u8; ED25519_PUBLIC_KEY_SIZE])> = None;
|
let mut x25519_public: Option<([u8; C25519_PUBLIC_KEY_SIZE], [u8; ED25519_PUBLIC_KEY_SIZE])> = None;
|
||||||
let mut x25519_secret: Option<([u8; C25519_SECRET_KEY_SIZE], [u8; ED25519_SECRET_KEY_SIZE])> = None;
|
let mut x25519_secret: Option<([u8; C25519_SECRET_KEY_SIZE], [u8; ED25519_SECRET_KEY_SIZE])> = None;
|
||||||
let mut p521_ecdh_ecdsa_public: Option<([u8; P521_PUBLIC_KEY_SIZE], [u8; P521_PUBLIC_KEY_SIZE], [u8; P521_ECDSA_SIGNATURE_SIZE], [u8; ED25519_SIGNATURE_SIZE])> = None;
|
let mut p384_ecdh_ecdsa_public: Option<(P384PublicKey, P384PublicKey, [u8; P384_ECDSA_SIGNATURE_SIZE], [u8; ED25519_SIGNATURE_SIZE])> = None;
|
||||||
let mut p521_ecdh_ecdsa_secret: Option<([u8; P521_SECRET_KEY_SIZE], [u8; P521_SECRET_KEY_SIZE])> = None;
|
let mut p384_ecdh_ecdsa_secret: Option<([u8; P384_SECRET_KEY_SIZE], [u8; P384_SECRET_KEY_SIZE])> = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
let algorithm = buf.read_u8(cursor);
|
let algorithm = buf.read_u8(cursor);
|
||||||
|
@ -426,23 +421,28 @@ impl Identity {
|
||||||
// compatibility. See comments in marshal(). New versions can ignore this field.
|
// compatibility. See comments in marshal(). New versions can ignore this field.
|
||||||
*cursor += 2;
|
*cursor += 2;
|
||||||
}
|
}
|
||||||
IDENTITY_ALGORITHM_EC_NIST_P521 => {
|
IDENTITY_ALGORITHM_EC_NIST_P384 => {
|
||||||
let size = buf.read_u16(cursor)?;
|
let size = buf.read_u16(cursor)?;
|
||||||
if size < Self::P521_PUBLIC_ONLY_BUNDLE_SIZE {
|
if size < Self::P384_PUBLIC_ONLY_BUNDLE_SIZE {
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p521 public key"));
|
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key"));
|
||||||
}
|
}
|
||||||
let a = buf.read_bytes_fixed::<P521_PUBLIC_KEY_SIZE>(cursor)?;
|
let a = buf.read_bytes_fixed::<P384_PUBLIC_KEY_SIZE>(cursor)?;
|
||||||
let b = buf.read_bytes_fixed::<P521_PUBLIC_KEY_SIZE>(cursor)?;
|
let b = buf.read_bytes_fixed::<P384_PUBLIC_KEY_SIZE>(cursor)?;
|
||||||
let c = buf.read_bytes_fixed::<P521_ECDSA_SIGNATURE_SIZE>(cursor)?;
|
let c = buf.read_bytes_fixed::<P384_ECDSA_SIGNATURE_SIZE>(cursor)?;
|
||||||
let d = buf.read_bytes_fixed::<ED25519_SIGNATURE_SIZE>(cursor)?;
|
let d = buf.read_bytes_fixed::<ED25519_SIGNATURE_SIZE>(cursor)?;
|
||||||
p521_ecdh_ecdsa_public = Some((a.clone(), b.clone(), c.clone(), d.clone()));
|
let a = P384PublicKey::from_bytes(a);
|
||||||
if size > Self::P521_PUBLIC_ONLY_BUNDLE_SIZE {
|
let b = P384PublicKey::from_bytes(b);
|
||||||
if size != Self::P521_PUBLIC_AND_PRIVATE_BUNDLE_SIZE {
|
if a.is_none() || b.is_none() {
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p521 secret key"));
|
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 public key"));
|
||||||
|
}
|
||||||
|
p384_ecdh_ecdsa_public = Some((a.unwrap(), b.unwrap(), c.clone(), d.clone()));
|
||||||
|
if size > Self::P384_PUBLIC_ONLY_BUNDLE_SIZE {
|
||||||
|
if size != Self::P384_PUBLIC_AND_PRIVATE_BUNDLE_SIZE {
|
||||||
|
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "invalid p384 secret key"));
|
||||||
}
|
}
|
||||||
let a = buf.read_bytes_fixed::<P521_SECRET_KEY_SIZE>(cursor)?;
|
let a = buf.read_bytes_fixed::<P384_SECRET_KEY_SIZE>(cursor)?;
|
||||||
let b = buf.read_bytes_fixed::<P521_SECRET_KEY_SIZE>(cursor)?;
|
let b = buf.read_bytes_fixed::<P384_SECRET_KEY_SIZE>(cursor)?;
|
||||||
p521_ecdh_ecdsa_secret = Some((a.clone(), b.clone()));
|
p384_ecdh_ecdsa_secret = Some((a.clone(), b.clone()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -460,27 +460,30 @@ impl Identity {
|
||||||
}
|
}
|
||||||
let x25519_public = x25519_public.unwrap();
|
let x25519_public = x25519_public.unwrap();
|
||||||
|
|
||||||
let mut hh = highwayhasher();
|
let mut sha = SHA512::new();
|
||||||
Hasher::write_u64(&mut hh, address.to_u64());
|
sha.update(&address.to_bytes());
|
||||||
Hasher::write(&mut hh, &x25519_public.0);
|
sha.update(&x25519_public.0);
|
||||||
Hasher::write(&mut hh, &x25519_public.1);
|
sha.update(&x25519_public.1);
|
||||||
let _ = p521_ecdh_ecdsa_public.as_ref().map(|p521| {
|
if p384_ecdh_ecdsa_public.is_some() {
|
||||||
Hasher::write(&mut hh, &p521.0);
|
let p384 = p384_ecdh_ecdsa_public.as_ref().unwrap();
|
||||||
Hasher::write(&mut hh, &p521.1);
|
sha.update(&[IDENTITY_ALGORITHM_EC_NIST_P384]);
|
||||||
});
|
sha.update(p384.0.as_bytes());
|
||||||
|
sha.update(p384.1.as_bytes());
|
||||||
|
sha.update(&p384.2);
|
||||||
|
sha.update(&p384.3);
|
||||||
|
}
|
||||||
|
|
||||||
Ok(Identity {
|
Ok(Identity {
|
||||||
address,
|
address,
|
||||||
fast_eq_hash: u128_from_2xu64_ne(hh.finalize128()),
|
|
||||||
c25519: x25519_public.0.clone(),
|
c25519: x25519_public.0.clone(),
|
||||||
ed25519: x25519_public.1.clone(),
|
ed25519: x25519_public.1.clone(),
|
||||||
p521: if p521_ecdh_ecdsa_public.is_some() {
|
p384: if p384_ecdh_ecdsa_public.is_some() {
|
||||||
let p521_ecdh_ecdsa_public = p521_ecdh_ecdsa_public.as_ref().unwrap();
|
let p384_ecdh_ecdsa_public = p384_ecdh_ecdsa_public.as_ref().unwrap();
|
||||||
Some(IdentityP521Public {
|
Some(IdentityP384Public {
|
||||||
ecdh: p521_ecdh_ecdsa_public.0.clone(),
|
ecdh: p384_ecdh_ecdsa_public.0.clone(),
|
||||||
ecdsa: p521_ecdh_ecdsa_public.1.clone(),
|
ecdsa: p384_ecdh_ecdsa_public.1.clone(),
|
||||||
ecdsa_self_signature: p521_ecdh_ecdsa_public.2.clone(),
|
ecdsa_self_signature: p384_ecdh_ecdsa_public.2.clone(),
|
||||||
ed25519_self_signature: p521_ecdh_ecdsa_public.3.clone(),
|
ed25519_self_signature: p384_ecdh_ecdsa_public.3.clone(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -495,17 +498,17 @@ impl Identity {
|
||||||
Some(IdentitySecret {
|
Some(IdentitySecret {
|
||||||
c25519: c25519_secret.unwrap(),
|
c25519: c25519_secret.unwrap(),
|
||||||
ed25519: ed25519_secret.unwrap(),
|
ed25519: ed25519_secret.unwrap(),
|
||||||
p521: if p521_ecdh_ecdsa_secret.is_some() && p521_ecdh_ecdsa_public.is_some() {
|
p384: if p384_ecdh_ecdsa_secret.is_some() && p384_ecdh_ecdsa_public.is_some() {
|
||||||
let p521_ecdh_ecdsa_public = p521_ecdh_ecdsa_public.as_ref().unwrap();
|
let p384_ecdh_ecdsa_public = p384_ecdh_ecdsa_public.as_ref().unwrap();
|
||||||
let p521_ecdh_ecdsa_secret = p521_ecdh_ecdsa_secret.as_ref().unwrap();
|
let p384_ecdh_ecdsa_secret = p384_ecdh_ecdsa_secret.as_ref().unwrap();
|
||||||
let p521_ecdh_secret = P521KeyPair::from_bytes(&p521_ecdh_ecdsa_public.0, &p521_ecdh_ecdsa_secret.0);
|
let p384_ecdh_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.0.as_bytes(), &p384_ecdh_ecdsa_secret.0);
|
||||||
let p521_ecdsa_secret = P521KeyPair::from_bytes(&p521_ecdh_ecdsa_public.1, &p521_ecdh_ecdsa_secret.1);
|
let p384_ecdsa_secret = P384KeyPair::from_bytes(p384_ecdh_ecdsa_public.1.as_bytes(), &p384_ecdh_ecdsa_secret.1);
|
||||||
if p521_ecdh_secret.is_none() || p521_ecdsa_secret.is_none() {
|
if p384_ecdh_secret.is_none() || p384_ecdsa_secret.is_none() {
|
||||||
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "p521 secret key invalid"));
|
return std::io::Result::Err(std::io::Error::new(std::io::ErrorKind::InvalidData, "p384 secret key invalid"));
|
||||||
}
|
}
|
||||||
Some(IdentityP521Secret {
|
Some(IdentityP384Secret {
|
||||||
ecdh: p521_ecdh_secret.unwrap(),
|
ecdh: p384_ecdh_secret.unwrap(),
|
||||||
ecdsa: p521_ecdsa_secret.unwrap(),
|
ecdsa: p384_ecdsa_secret.unwrap(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
@ -514,6 +517,7 @@ impl Identity {
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
|
fingerprint: sha.finish()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -521,32 +525,39 @@ impl Identity {
|
||||||
pub fn to_string_with_options(&self, include_algorithms: u8, include_private: bool) -> String {
|
pub fn to_string_with_options(&self, include_algorithms: u8, include_private: bool) -> String {
|
||||||
if include_private && self.secret.is_some() {
|
if include_private && self.secret.is_some() {
|
||||||
let secret = self.secret.as_ref().unwrap();
|
let secret = self.secret.as_ref().unwrap();
|
||||||
if (include_algorithms & IDENTITY_ALGORITHM_EC_NIST_P521) == IDENTITY_ALGORITHM_EC_NIST_P521 && secret.p521.is_some() && self.p521.is_some() {
|
if (include_algorithms & IDENTITY_ALGORITHM_EC_NIST_P384) == IDENTITY_ALGORITHM_EC_NIST_P384 && secret.p384.is_some() && self.p384.is_some() {
|
||||||
let p521_secret = secret.p521.as_ref().unwrap();
|
let p384_secret = secret.p384.as_ref().unwrap();
|
||||||
let p521 = self.p521.as_ref().unwrap();
|
let p384 = self.p384.as_ref().unwrap();
|
||||||
let p521_secret_joined: [u8; P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE] = concat_arrays_2(p521_secret.ecdh.secret_key_bytes().as_bytes(), p521_secret.ecdsa.secret_key_bytes().as_bytes());
|
let p384_secret_joined: [u8; P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE] = concat_arrays_2(p384_secret.ecdh.secret_key_bytes().as_bytes(), p384_secret.ecdsa.secret_key_bytes().as_bytes());
|
||||||
let p521_joined: [u8; P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(&p521.ecdh, &p521.ecdsa, &p521.ecdsa_self_signature, &p521.ed25519_self_signature);
|
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||||
format!("{}:0:{}{}:{}{}:2:{}:{}",
|
format!("{}:0:{}{}:{}{}:2:{}:{}",
|
||||||
self.address.to_string(),
|
self.address.to_string(),
|
||||||
hex::to_string(&self.c25519),
|
hex::to_string(&self.c25519),
|
||||||
hex::to_string(&self.ed25519),
|
hex::to_string(&self.ed25519),
|
||||||
hex::to_string(&secret.c25519.secret_bytes().0),
|
hex::to_string(&secret.c25519.secret_bytes().0),
|
||||||
hex::to_string(&secret.ed25519.secret_bytes().0),
|
hex::to_string(&secret.ed25519.secret_bytes().0),
|
||||||
base64::encode_config(p521_joined, base64::URL_SAFE_NO_PAD),
|
base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD),
|
||||||
base64::encode_config(p521_secret_joined, base64::URL_SAFE_NO_PAD))
|
base64::encode_config(p384_secret_joined, base64::URL_SAFE_NO_PAD))
|
||||||
} else {
|
} else {
|
||||||
format!("{}:0:{}{}:{}{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519), hex::to_string(&secret.c25519.secret_bytes().0), hex::to_string(&secret.ed25519.secret_bytes().0))
|
format!("{}:0:{}{}:{}{}",
|
||||||
|
self.address.to_string(),
|
||||||
|
hex::to_string(&self.c25519),
|
||||||
|
hex::to_string(&self.ed25519),
|
||||||
|
hex::to_string(&secret.c25519.secret_bytes().0),
|
||||||
|
hex::to_string(&secret.ed25519.secret_bytes().0))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.p521.as_ref().map_or_else(|| {
|
self.p384.as_ref().map_or_else(|| {
|
||||||
format!("{}:0:{}{}", self.address.to_string(), hex::to_string(&self.c25519), hex::to_string(&self.ed25519))
|
format!("{}:0:{}{}",
|
||||||
}, |p521| {
|
self.address.to_string(),
|
||||||
let p521_joined: [u8; P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(&p521.ecdh, &p521.ecdsa, &p521.ecdsa_self_signature, &p521.ed25519_self_signature);
|
hex::to_string(&self.c25519),hex::to_string(&self.ed25519))
|
||||||
|
}, |p384| {
|
||||||
|
let p384_joined: [u8; P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE] = concat_arrays_4(p384.ecdh.as_bytes(), p384.ecdsa.as_bytes(), &p384.ecdsa_self_signature, &p384.ed25519_self_signature);
|
||||||
format!("{}:0:{}{}::2:{}",
|
format!("{}:0:{}{}::2:{}",
|
||||||
self.address.to_string(),
|
self.address.to_string(),
|
||||||
hex::to_string(&self.c25519),
|
hex::to_string(&self.c25519),
|
||||||
hex::to_string(&self.ed25519),
|
hex::to_string(&self.ed25519),
|
||||||
base64::encode_config(p521_joined, base64::URL_SAFE_NO_PAD))
|
base64::encode_config(p384_joined, base64::URL_SAFE_NO_PAD))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -573,7 +584,7 @@ impl FromStr for Identity {
|
||||||
}
|
}
|
||||||
let address = Address::from_str(fields[0]).map_err(|_| InvalidFormatError)?;
|
let address = Address::from_str(fields[0]).map_err(|_| InvalidFormatError)?;
|
||||||
|
|
||||||
// x25519 public, x25519 secret, p521 public, p521 secret
|
// x25519 public, x25519 secret, p384 public, p384 secret
|
||||||
let mut keys: [Option<&str>; 4] = [None, None, None, None];
|
let mut keys: [Option<&str>; 4] = [None, None, None, None];
|
||||||
|
|
||||||
let mut ptr = 1;
|
let mut ptr = 1;
|
||||||
|
@ -607,30 +618,38 @@ impl FromStr for Identity {
|
||||||
if keys[0].len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
|
if keys[0].len() != C25519_PUBLIC_KEY_SIZE + ED25519_PUBLIC_KEY_SIZE {
|
||||||
return Err(InvalidFormatError);
|
return Err(InvalidFormatError);
|
||||||
}
|
}
|
||||||
|
if !keys[2].is_empty() && keys[2].len() != P384_PUBLIC_KEY_SIZE + P384_PUBLIC_KEY_SIZE + P384_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE {
|
||||||
|
return Err(InvalidFormatError);
|
||||||
|
}
|
||||||
|
if !keys[3].is_empty() && keys[3].len() != P384_SECRET_KEY_SIZE + P384_SECRET_KEY_SIZE {
|
||||||
|
return Err(InvalidFormatError);
|
||||||
|
}
|
||||||
|
|
||||||
let mut hh = highwayhasher();
|
let mut sha = SHA512::new();
|
||||||
Hasher::write_u64(&mut hh, address.to_u64());
|
sha.update(&address.to_bytes());
|
||||||
Hasher::write(&mut hh, keys[0].as_slice());
|
sha.update(&keys[0].as_slice()[0..64]);
|
||||||
if !keys[2].is_empty() {
|
if !keys[2].is_empty() {
|
||||||
Hasher::write(&mut hh, &keys[2].as_slice()[0..264]);
|
sha.update(&[IDENTITY_ALGORITHM_EC_NIST_P384]);
|
||||||
|
sha.update(&keys[2].as_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Identity {
|
Ok(Identity {
|
||||||
address,
|
address,
|
||||||
fast_eq_hash: u128_from_2xu64_ne(hh.finalize128()),
|
|
||||||
c25519: keys[0].as_slice()[0..32].try_into().unwrap(),
|
c25519: keys[0].as_slice()[0..32].try_into().unwrap(),
|
||||||
ed25519: keys[0].as_slice()[32..64].try_into().unwrap(),
|
ed25519: keys[0].as_slice()[32..64].try_into().unwrap(),
|
||||||
p521: if keys[2].is_empty() {
|
p384: if keys[2].is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
if keys[2].len() != P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE {
|
let ecdh = P384PublicKey::from_bytes(&keys[2].as_slice()[..P384_PUBLIC_KEY_SIZE]);
|
||||||
|
let ecdsa = P384PublicKey::from_bytes(&keys[2].as_slice()[P384_PUBLIC_KEY_SIZE..(P384_PUBLIC_KEY_SIZE * 2)]);
|
||||||
|
if ecdh.is_none() || ecdsa.is_none() {
|
||||||
return Err(InvalidFormatError);
|
return Err(InvalidFormatError);
|
||||||
}
|
}
|
||||||
Some(IdentityP521Public {
|
Some(IdentityP384Public {
|
||||||
ecdh: keys[2].as_slice()[0..132].try_into().unwrap(),
|
ecdh: ecdh.unwrap(),
|
||||||
ecdsa: keys[2].as_slice()[132..264].try_into().unwrap(),
|
ecdsa: ecdsa.unwrap(),
|
||||||
ecdsa_self_signature: keys[2].as_slice()[264..396].try_into().unwrap(),
|
ecdsa_self_signature: keys[2].as_slice()[(P384_PUBLIC_KEY_SIZE * 2)..((P384_PUBLIC_KEY_SIZE * 2) + P384_ECDSA_SIGNATURE_SIZE)].try_into().unwrap(),
|
||||||
ed25519_self_signature: keys[2].as_slice()[396..460].try_into().unwrap(),
|
ed25519_self_signature: keys[2].as_slice()[((P384_PUBLIC_KEY_SIZE * 2) + P384_ECDSA_SIGNATURE_SIZE)..].try_into().unwrap(),
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
secret: if keys[1].is_empty() {
|
secret: if keys[1].is_empty() {
|
||||||
|
@ -654,25 +673,19 @@ impl FromStr for Identity {
|
||||||
}
|
}
|
||||||
tmp.unwrap()
|
tmp.unwrap()
|
||||||
},
|
},
|
||||||
p521: if keys[3].is_empty() {
|
p384: if keys[3].is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
if keys[2].len() != P521_PUBLIC_KEY_SIZE + P521_PUBLIC_KEY_SIZE + P521_ECDSA_SIGNATURE_SIZE + ED25519_SIGNATURE_SIZE {
|
Some(IdentityP384Secret {
|
||||||
return Err(InvalidFormatError);
|
|
||||||
}
|
|
||||||
if keys[3].len() != P521_SECRET_KEY_SIZE + P521_SECRET_KEY_SIZE {
|
|
||||||
return Err(InvalidFormatError);
|
|
||||||
}
|
|
||||||
Some(IdentityP521Secret {
|
|
||||||
ecdh: {
|
ecdh: {
|
||||||
let tmp = P521KeyPair::from_bytes(&keys[2].as_slice()[0..132], &keys[3].as_slice()[..P521_SECRET_KEY_SIZE]);
|
let tmp = P384KeyPair::from_bytes(&keys[2].as_slice()[..P384_PUBLIC_KEY_SIZE], &keys[3].as_slice()[..P384_SECRET_KEY_SIZE]);
|
||||||
if tmp.is_none() {
|
if tmp.is_none() {
|
||||||
return Err(InvalidFormatError);
|
return Err(InvalidFormatError);
|
||||||
}
|
}
|
||||||
tmp.unwrap()
|
tmp.unwrap()
|
||||||
},
|
},
|
||||||
ecdsa: {
|
ecdsa: {
|
||||||
let tmp = P521KeyPair::from_bytes(&keys[2].as_slice()[132..264], &keys[3].as_slice()[P521_SECRET_KEY_SIZE..]);
|
let tmp = P384KeyPair::from_bytes(&keys[2].as_slice()[P384_PUBLIC_KEY_SIZE..(P384_PUBLIC_KEY_SIZE * 2)], &keys[3].as_slice()[P384_SECRET_KEY_SIZE..]);
|
||||||
if tmp.is_none() {
|
if tmp.is_none() {
|
||||||
return Err(InvalidFormatError);
|
return Err(InvalidFormatError);
|
||||||
}
|
}
|
||||||
|
@ -682,37 +695,20 @@ impl FromStr for Identity {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
fingerprint: sha.finish()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Identity {
|
impl PartialEq for Identity {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn eq(&self, other: &Self) -> bool { self.address == other.address && self.fast_eq_hash == other.fast_eq_hash }
|
fn eq(&self, other: &Self) -> bool { self.fingerprint == other.fingerprint }
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Eq for Identity {}
|
impl Eq for Identity {}
|
||||||
|
|
||||||
impl Ord for Identity {
|
impl Ord for Identity {
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
fn cmp(&self, other: &Self) -> Ordering { self.address.cmp(&other.address).then_with(|| self.fingerprint.cmp(&other.fingerprint)) }
|
||||||
self.address.cmp(&other.address).then_with(|| self.c25519.cmp(&other.c25519).then_with(|| self.ed25519.cmp(&other.ed25519).then_with(|| {
|
|
||||||
if self.p521.is_some() {
|
|
||||||
if other.p521.is_some() {
|
|
||||||
let p521_a = self.p521.as_ref().unwrap();
|
|
||||||
let p521_b = other.p521.as_ref().unwrap();
|
|
||||||
p521_a.ecdh.cmp(&p521_b.ecdh).then_with(|| p521_a.ecdsa.cmp(&p521_b.ecdsa))
|
|
||||||
} else {
|
|
||||||
Ordering::Greater
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if other.p521.is_none() {
|
|
||||||
Ordering::Equal
|
|
||||||
} else {
|
|
||||||
Ordering::Less
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialOrd for Identity {
|
impl PartialOrd for Identity {
|
||||||
|
@ -738,7 +734,7 @@ fn zt_address_derivation_memory_intensive_hash(digest: &mut [u8; 64], genmem_poo
|
||||||
let (genmem, genmem_alias_hack) = unsafe { (&mut *slice_from_raw_parts_mut(genmem_ptr, ADDRESS_DERIVATION_HASH_MEMORY_SIZE), &*slice_from_raw_parts(genmem_ptr, ADDRESS_DERIVATION_HASH_MEMORY_SIZE)) };
|
let (genmem, genmem_alias_hack) = unsafe { (&mut *slice_from_raw_parts_mut(genmem_ptr, ADDRESS_DERIVATION_HASH_MEMORY_SIZE), &*slice_from_raw_parts(genmem_ptr, ADDRESS_DERIVATION_HASH_MEMORY_SIZE)) };
|
||||||
let genmem_u64_ptr = genmem_ptr.cast::<u64>();
|
let genmem_u64_ptr = genmem_ptr.cast::<u64>();
|
||||||
|
|
||||||
let mut s20 = Salsa::new(&digest[0..32], &digest[32..40], false).unwrap();
|
let mut s20 = Salsa::<20>::new(&digest[0..32], &digest[32..40]);
|
||||||
|
|
||||||
s20.crypt(&crate::util::ZEROES[0..64], &mut genmem[0..64]);
|
s20.crypt(&crate::util::ZEROES[0..64], &mut genmem[0..64]);
|
||||||
let mut i: usize = 64;
|
let mut i: usize = 64;
|
||||||
|
@ -803,6 +799,7 @@ pub(crate) fn purge_verification_memory_pool() {
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::{Duration, SystemTime};
|
use std::time::{Duration, SystemTime};
|
||||||
|
use zerotier_core_crypto::hex;
|
||||||
use crate::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL};
|
use crate::vl1::identity::{Identity, IDENTITY_ALGORITHM_ALL};
|
||||||
|
|
||||||
const GOOD_V0_IDENTITIES: [&'static str; 10] = [
|
const GOOD_V0_IDENTITIES: [&'static str; 10] = [
|
||||||
|
@ -818,25 +815,40 @@ mod tests {
|
||||||
"aec623e59d:0:d7b1a715d95490611b8d467bbee442e3c88949f677371d3692da92f5b23d9e01bb916596cc1ddd2d5e0e5ecd6c750bb71ad2ba594b614b771c6f07b39dbe4126:ae4e4759d67158dcc54ede8c8ddb08acac49baf8b816883fc0ac5b6e328d17ced5f05ee0b4cd20b03bc5005471795c29206b835081b873fef26d3941416bd626%"
|
"aec623e59d:0:d7b1a715d95490611b8d467bbee442e3c88949f677371d3692da92f5b23d9e01bb916596cc1ddd2d5e0e5ecd6c750bb71ad2ba594b614b771c6f07b39dbe4126:ae4e4759d67158dcc54ede8c8ddb08acac49baf8b816883fc0ac5b6e328d17ced5f05ee0b4cd20b03bc5005471795c29206b835081b873fef26d3941416bd626%"
|
||||||
];
|
];
|
||||||
const GOOD_V1_IDENTITIES: [&'static str; 10] = [
|
const GOOD_V1_IDENTITIES: [&'static str; 10] = [
|
||||||
"a145374edd:0:552f4f3244d1cef146dfbf781e4a64d1b529b8a30b496100dbd4f2e60553280f4089f1a0eea27821991604d7bd8de93c1c24fd172558287c73c72a313865fe5f:c845de3ec427903e97feec496698b83d130a0fe03fe165f53f3c3dd1b86c1d4d808936369637e1d023f25b2081176d31ca1cc828c1b135da23eca5aa37476813:2:AItC2JQyFwA10ijnEmkPqNHKlH5L-nl7HiPA9bF6DlL6-qST7OALZhgDWTFjR3oqjTaxTwqeq4f9UUOZasMlSqw0ACV1H-JFhORbhzMu5Koj4Ufe6c7QZvpVrmPp6Ka2QsVQOkoca3E-hD94IJqA01FYA4tHtvW5X32tdg5YKFFNjXs5AFSm9MQeO21kIWTG-Xyu-BGGRvOTK3f0DXsjS3tiIWiECy0HIJaNBDaSg3qme7wvQ-v2xAiHt8OtI_vL6_-vpUbNAAknWgKeoP_ZTrtd4ReyKPWT_cP_lv6ba9x9FReNEnYNaAIXSqNFpZIeIBF3YMw33xabt4vKLFBaGW_2hGqzqWOhATyWjq077SLpCRFNqm_s6VODhowOsOHX6IUhYsLsE-f9t6jPykhEJsYEkqwh2yBVMAL4kTyfK_18o7s01_Ju8xsMADW7PJAGgypm4MTxNiODpHdnKqAxyXJblNPA2b9mv9YgOScDlYLg6gS2pk0od1CezPgLhEulOdaCfr8MddsW4_WH_I9wy2xuLAUQZdoZH5l5pdgOuForVSp2vgCYiXEmZfc1ybl81m5QnstUef6NQe9iwKJBd09A_tC11kGID-SaCQ:AdXjxecCo3AW9X-H4RIfvQiOItl4aESgTQem5RvWPlp5mNYi_AmPXdwl2ay5Ul1Ev55W3lLHuxK4kpzWH4pMdOktAAA-UD-xk-4cvGvE5VFo8XV9eGf0rsTXMYFx66GM5nqhMY7SqwNpi-CRPNzzwzOEeVj5J_monJlTd46pO0nkOz7j",
|
"7c2b729f7b:0:4d9d097668f27decb7a56953cf560f595e13c48873c40c4dc19dc5a716aa551e7c0b91d00f2af1bcaa7e31f5cfe38077eb55aad97dcbb0f3b013b5297a93aa2d::2:A4Ro-uv9LK9Je7RHDOAzV_RbrG-rZ6v7_7ePKzfQk_uo0-avhSlkT8NR408L6a5cvQIDhDVhBWYR_eAaTrE888Val9FSMaM_ekmle6k74V7fi0fcA5Jhsg-eHfyw1z_ovGpWppJEs0xX5SMOCM2MBs93K20e9P4IpdFXayoLod9FAKAn-arh94tbkUALr4Uf59ACLovMymhoJr6Y-wkKRwEsnAOdSNa8bN3HGGVDOykAgvLs1E65pkIkFXTxmcSVMLbETKETYwmSaaNYEXMVGD5HBJ0GqA8298bs4HVYjNoYwMHu--Hwb1joQS0XK5WjifYRrWAA94J48f05tMDklQcF",
|
||||||
"3fb14dca24:0:bf46edea78200f176766c6b9c96ec68bc513908b974db5f877a0c9789a26ea4dd4fe1326fba39fc1bf37de1053948a200770ee2d145fb7f074f83b243f26b30e:406d507510278d9d3a1b8bbe183a9cbc054d6eefb4e66ea38bb85203aed1a74502782fed8a3b12060fcf311f2b12dc006f3680e091b9a7ef8eecd0dfad91ad57:2:AJM7WjBgO24HVjoSQ2TXaKvH5krzZEeIekF_vV_A05-s30j0EJK0ZcVB-RJtt410JOmxA_wpdvp6m5CZdoDi_R6PAJ66ah7noFWdJ_BZ8tV4Q5AK15rA9AvmONZLoxvDAsmWFzHPOeAQ-nWCfFDqPNiI1kTRc3gOIzheOFZ4Esy-AjwuAEvqydx15Y38oC-B1TeS5Ey1fCFqo19R4f-c2hxIUM_6TjPYPFWuM26Q06SyAkedetjiSAkj95tSLqB9Xb4UXCS2AGZqnOLT2tq5pE9XlUAze3K8e9ubtThKOYLzUM1kxYDA8UqxeIf-JFIv-ukcpB2XL2J0vcoeaI0Ki-a205inXX78AJ6BQJut63ovH46OfGP0YkKnY7VdX5N6bZGLONHxtCd1JCVphqa60ze3Yh2nkCSPHGxK6LJ-kTO17ypGcy6C6ztbATj_yXeADyZAbfjiWLDDJXngYWMGu3-gp0gdQM6iv2vuLlrQ8BrBWR9W77xF6mHjPTCo58YBHtu86zdoY5ecBFMwbXrpwSYb_m_UvQXYbY1EcyTT5A5NPkjvE4wd6J48dj-0M5te58v2RBVDLZkDzT7e86vUeKrxABzk2YPr6jLUAQ:AABH1HPwGnfdyciVwch0PYH67utoUtk4XxqmPiNZx9b9oUepuv22rlBd0JdnCgPbvMjfj7MygtneVG48pDbeMe28AYOOjZGjHNZ5cKxQIoOzYuKPDuLY67BcQOOUBFKsmz9cRyCe8sQsc3HtnyYDcLcNCVS3c2Hi529xAvX3qhjuxh-d",
|
"0d4be14d60:0:69c300b00e5af62e2d55c1ea81f5316375038608ad3696f67bf092f960e4f7036e51a091c88c1bcadf00cf43647e9464b53017a760e24deb76f776488d430237::2:ArquLyV1f3eaXkFZPdRHzgEhD01HK0tgzXBiWO5a1flT1agj_lkyNziY-YyOoP1fEQKplNnnXhryqI75siIZFMMUZInzlJm1Ll84_qV2pfbcFMXNN--4P5LgtChR1DNNHaKLMHt5U0IKh9UwSvfH5xbTr8LjadezbPOuy9a-uf-FSQTBZvALYtLSKLjPvaNMLMamT6ruEuJbgRLsCeZ38zEqMPYQ05iG4_TrjAw0uZJ4pz8m--STLE6B5XOU3Lc50fzxdDrIqL-dDTbCX5XecQzDiN8a3CSf8vq2uMu3-Fs6AaQ_dNc8_B8Y2dpfc7UYj7sRmAyFiw0FEwHIPPSZDjsI",
|
||||||
"467ca2d39c:0:5836b22097530a21999b17dc1bade971e8c8d92f653da1428eaeeb1c7d25296506081a3660f968a81463d75b86fc8f01455c6d86ff13aac28a63d5aef159645f:d0d23ddad71bf39ccaea2bca7ab777100632b3904a077d6f5e5bbf580b32ef50ced7be0090ea6d8775e62bd11acadcacae6effc2e162bf9e694d2b3c498c9d26:2:AE2mcfDzyoksMh-uhinx5BYTTdLgbQCq9Tzfhb5-MdJKIZC0drgBJLAsQTlL1DEjWdSqJGC3c5J__Ydu3Q78qgU5AKUu-YpMKx8gK1EYYNnJLOiN5vxSdViOmUX91_4YxpbDXPWAXoGynKlW0XaHPk6jtrvlZcvOY08BbOc6-3XckKQwASx8yJ13H_wwnuAEhm_9Mv6tgFVroR2cWfn1AtZKoPdpfMg0u8nghu4ULfiyiusXcxc9I9c6wedr_gk1F2O1_y9bAA_pdFwN-JuHRO0QuU4UDC9cS4G3Gv8ZZHgNIswgjWWMvwrE6kcBtvoP5pgPYFTqaxQ5s_86qe7EIW6vF6InjLk_AfkaAfFjZ2tgbjh2u-tWwPmieYcRCyvifZ3pCyFpNtCy5ILhXgejUWg68aEqTgTLiL__2Jg3V4bTL7TpWWiWmNnnAeN9uGHULx3AJD-I-Uq1A2hNldlwe0pmRSc8VLQld5EClXM_RIh_WtkOwUB0yG1N6rfbtXf7wY-Pq21DzsDFezNukf97fACJfoIRiH73gHCyizM4_NkbbQL9W_7ehsVzvIJBqfyGd2BxuqxRxVPL_SkoACOo9BsWUXyMDCPWzFMTBA:AU3cy-juui20tWVrYndPqsmeJmQoSJtxvyr0RPjb6Zr5wg7q8gFWgdZiPVRh_oz3-k_upRUNaGFbpW6CW-d9P8NcAamCreeQ5m42hdzd5x5ao8ouE-aR9aBhBFOfusPAIgqm53857KwtWwoaRjkRo4RH-_oZXEKn6sG16XFFs-rBlG7p",
|
"d2608c9625:0:5a19103678e8a28942d077278024421219c5dcf6e25fd8d74c082df6e288ec581ac09d4bc84b9ef2287342c1b4736da59109d9986740a9d135621399569161cc::2:A5T6RjeDlzTvJAFHlXfSFP3dOPWxCaE1jRcAdzZgxsdy80TV7pVRiAyeOdYGJEbFaQPa0qIl20ZHXUI5fZfUc7PWxp3wkQqkqD9X-3Ms3wOPP5kc_kqxKIGnlXk6E_NEggZIPr0LONZShGrDRO-kKsdDxzP9EE4FDlnrD0E2yAbjH8hAIQqhERarSabCNE_iJrJG30yi0bPZtJNI36SPJex11X8X7Vu8GFXqWmZQRIU9Q1WJBmX-LVwxxD4BzNKWsy_txeQeGokKGydV3ejASi6ViicbwxtkVHbPzDgYl0kOBwv_qJTg3PIt1x27qTAYPs4qG6PSWQt_eqGtrKCYVOAO",
|
||||||
"344397cd0e:0:3f847dfd3b747bc5f9da128945e9b36199dc742c7a51bb2590cbeeb0eb3ef07b5fa68a7a97b7d13fd6d4dc122f3fe71094cc177456c3d92e05c191e18ef78f01:c88e4895defd13ae763e68cff50e7406c6b601f18c1a24406ecfe9392a9f9c50206194a29bdfc1b256547407540ca14d91d3c2069515c754dcd5065fdda64032:2:ACXUR9j2GTh2jEyCDKj5qmAdI2XVMHeQMJYnpTVL3dVlODbrTPOc326YQ-vxNmIvIQa5KYPqhYIqbYfLeeKv1wWaAMmkPgmcLNKz2lZ3GqE-dvQ4--rUOWITIpSTcg1Kwo1o9twmU5ynCyah826-xhcJnfWg19AzPqR6Puwh2IGj9Q7oAGMVoTob4feGAj0G90nD4V-Uylqyb-bh83NWo61jsUm42UGptsq397LTR3oM3AzqNV_ntO1Aiq-exy6zBVizOMJfAFXJx6pc8CzeS-pPeg3h_e4MKU7_GZGayrIbEoN8SUoBrl7yonKkfm2geSNCDf3fy9VXYcKvdGlZKSvGpxQ8p9PBAQIc8gBz1iCfggx3XCMd4d3cRMdsbxmlp0vlb9DRKbxakAlzcz6g92wUJWsZ1b6p5F7b9glyL4BezlPyGxPFNHHNASpkkx1YsGdjm_KhllbAzeIBwLd4ghwsRXKzFQ2Gm2lf6CjVECDAY77pe4oX4RJTZPvBSC1sWMndBTqqpioxMYOHY9iVC6im7i44nUrW3q4OSe7PfC5LRN7fExpbq5czH9bQ9FS_uxY9v-gDiL9MdLNNs_o-iLsy3u6vq0mosn5nBQ:ACZPO033kuvXj-vtlimDWcdTj-aLRo3VlAUd2PduaakvxdVHoBN7wIvWFlZRbi7rx842vS49tZAQtIvPSnEUMms7AZc2iXkvAFvQiecTKN3Dsa-iOX0zf8f-cUHK2OJ0VVkp4XoXHvVeL_Me-l1MUZBUEQVx2uYVrU7uPxext8qqpeRc",
|
"7615cd90d0:0:49641eb54b001b1c91afd162a780252bea7e315c7454beda712864e5ad801046eedb11927cdef951c64c12706cc4f332512307de9754d99ab1978b396d5c70a2::2:A-KmSE-Cga8As0AT25LxJTOLntyN1ORiJ6it_Ud6CfGPbANpvEnAbs2m7i7yJ_DP9APo4Q9RrQKS5THa0yADD_QWLz-o7zb4cTfAoG0IkVed2sWWq7SR0-6VHQWg__WglqMYIKM4FXPZ9i1uuKLrshN-fF4XAdqJLrqWTpqvpVmdYJB9LKez5MpEXO-UeLJb-4jgeFjB35Qxbcp6TsVZca3ep5FzqYdyNjfI7RkKTPnVpCKdTWFmIbDqzQMyjqg93lu8OWq2tPorzUth-qpWf9AWTPYxofNzd_t9OMimR6kevaG-qjsEX7-SOzf-UI76y3JR6lF9dMs3UiqVjv3guDgE",
|
||||||
"33c9267872:0:8a50ee533b2fe15a03ab351a26f0347cdaa087f66093b71005de4660202e7932a41dc38d8c47f38b1d0adb39446b1d58f8f5eb1f1d28ca2b6bb7f7ef0bb22fdd:00e1a9b356a1f5f28515df24015623fa09f5fb72e83de115a66dfe7222b8266fc37f0bc8fa948e80e40067246b315b275e31d045622d047b2a4de55161b089b2:2:AOO1k926VACCHyWxMkCRZheDch28mvwN6jW46yDGm7gPQwroB6Yy9VaftCWNKa3togYSLTfowEac-0v9dBns7HHcABbJUmsAJ4038GoIeXsFIBSYP_uvNFZN4Xn4ZtyHeRCZDmR2rKaGB9hjMEZ6XkdtV17UmFLjlO9VG1SosOZJQhvVAWSbEbDyo7kIynMJmo94pyp7ZDYzWiZ8ubfhXZNAxnTNpdz252s3__O3xUUQcHr-gmKLVXGnHfPrFrG0XhLyZH-oAHK2jZ-FhirXn7v7gyW3JA6s_S-zBYb-qdY6ZlTM0WpT0kys_AJ9fYY7TB3AS0q27Ag-swg-jGf5qedcXpYUg7GJAQ6Tq__uN4ILH4NryKq6RQArqdTxB6tmCSTkjnFbZ4YrDUX-_eHB-i9oisZoIRz3pjEYR3EcY52hn6EI1sRPxFQOAPPxSW0vGqr5JMM8sZp7S-jUmVTnu742e9m9dfpO7bEPnPaeEhJUFVzPvLKeIrkhbug20hd5CkClRgiUgJXQ3IpQoDes1t1aGL8uZudFknGsm3QJXpw6Tb5h8ZSuQj6bueWw7QdNcHV7fA0UTJpy41lgx4WZ7xv2-M0jkOhmliu9BQ:ABtZ0WVVe7SywNQ1oVeh4VIXkk0IT0ML8kUwy4GnCGCKLOit084F14eoDss0okk248jDJEjiRTmmSXQbC2I_SrVZAXL-Q-MhOUuHdiR_ES2YATalcWfDH534_g6D6MI1l-dJ7HyQu9Oz-xF1SMiP6-XhvS9vnmMBuyNkirbI7c1eGE5Q",
|
"bf7544ad9f:0:fb726a8e1c8c50100661ca64a99504b13ad7d0604db1c96c35f94091aaed69168e118aa7c3ca7f4aad092a02513ca171ca5fdc445f1bbc2dda49e8af544f941b::2:A0NV5tU0HTcdrJQzjgGy01FbHzi0v7DnpkyrmNvGrcJwcU6Fas35vmpNBJpsi9YTRQPNCzPYkeMz_7s59ukEzK-Lhk5ZxyvGdJMUoW6mmWiE8T6pJcD3JC37kNRuW28cH13d5_YuDnVsPz-p1ER2d2BzvjucwgkhmQnMSWSVCNikfe7pR-2mqFjr2KkEHqCp5js1tdM-nzOrV7smjed1vl2AZIeYRBcpJXBCDiyCMSpe8MD32KcNoJPINHu6k7uDxKKEwSocacDAI8SlFjz5Sb_JXM0XUDZyoSrql9bAdDi4s4bVVjTrpOqcUfQ9Shs-QKNcrOmDG-uyM4Pqaz5NRhsF",
|
||||||
"00ee1db06b:0:3d60bd6ef991ee621cc77573407d6be66bd60dbd82f2ada4a28afafcf8b00c6a626ed3d000a4bc2630a013fa4f25a8e60116bdda14bd1380e48a3360e9eff6f0:603314e715652db9b8e0b2de217051a9a017962e7349a94ad2f4f1787f3e1c66b881c47c73a8dcb7d020b13e4a5858b0cfc392ce348e83c1a2c4c76471346ea2:2:ALEHh95ZRAP1I5lahFTXRa_51YeG0l3t6vroG-q1YFpTnIYTN4JajBBXmQhmTf9hHDc-R9iQstPXRV4FPsFmCihwAOAenFhNDDRlr7otUP5o2T-Oc3yX3MEZZa3jY8GxSuL_2aVQEcYgPfWrierDiXi7QQTGyvmkt-wMy414nPcRTP2NAaEys8AKMRISLFh631kJ_b-_KdViUmAnAMcxG7zVu-tr2Zi00tZ2i8KY_9LCXofEtUdGTg29CqqkNlMX2y0cRz6pAHXWIUNqq7EVy6DzAUEnMZvcKW-Ig2zJW-yOOvPsIQZ_naOOquyFSyQoRGfvVajI6CIjmwgBaOL1hD1EG50AoVu3APwCbvUhRgU-48s0gqY9fQ5buC9Gy-cH78qmxKv5a0kZWSeXtu6J8CeH68tCd8yZhgqA4c4TBc1T6QE5NSJRbmM8AMF_O2TBwoMwWhs5KZ7ljEX5L4RwbBu79g3R_3ub8s8iQ9wHuSyZDlICDlbH__pYY0jBwNIwH6wOnyp9bZ9u610ltU-spahjDy4Kh1W_J3-N35dT6psg234-ctpEKoj264kG7WsXOOohRsdSEGqgFuFlKNclevdx4fQsKzsXQkmCDw:AXBt-nAitsr0E_kjc8DhvMVXLOXjKnz9cK9t--mm6_iUmYnAjjVzkvIjS9_2r3ba9UM2vWJ5et9EvOj6rZfZjhkJAZ6tGwq3TrANuzn88lQq845i18rI5VWjMYJLQIl-_uQC-5vt8VJrNYAwPay_o2LV73rMcWMroNFZy6U0sLB5qOs_",
|
"e7ea5a5db4:0:1fc5c90b7350899babf0e20b3aaf7d778d63cd8b587f966e194841e80fc69a73a2c2ae340ae78b5aea47733352eddbd501147d464ef7d31297832a4a80473c40::2:Ahq6VPPeoqNX-SbAuxg2GjgxJw_4dJQb7hCL_hZ3ZZD8QHey5oK8rEg8U5GBT0M92wK4SXrUTJErbn2xxL5mFfzq4Yv_C5V_y88LYDV_pAZ5vA4HVzIs1dYXrKJZzgt9CwHev05MbvOguc3ueUGxYw5ZVR7l9RM-O4CXgvpWre8kuElv6nXfT6Rrs7UFHgJqpdti93vDMBiY6_4_sk68OFDTakqixHO0qxPwWHDOFW9OWBDCeWuDtpv3VLhCYspGKborJMbraQkkXwkd04Tm5-UgsXFfXRXP_qR4RBLmxFmf7kuSHQzqTJJQ-kIw0ZUKqvNCjqP1ii2LIa3YcXJjNZQF",
|
||||||
"50cd888fe0:0:577a71938a275240667d381b80fe4eb4a99a49d564f2bd32d6b4aa58fe2db832837c321d02f2c9f1ac5228572f6446c3706e09c7c81340450ebcc42c5cde50a8:d0f23d0a727636bdf9d8ff7a2ac4836972e0f1a754cdb6d70f070261f5f2f154d17fa6424f2cd2da9cdaca18eeab11181b40463c8070f4f5f22b286f2040cecd:2:AacV20WYy_3mvFze-EdowUu4w1K_tcvy6U1iU62k5UdvgdWdpPHY-vXDMffN-Hs8eYes9rxucBQTTL7oxIs3L6u8ANDr4kd6ErsTuK7ewI597clD51pi9B7QFPRf_KxktFGQvVGY2IYbZ8tS4vRX1MKPdwojwea02QT0N5aDqH-NXv-nARfC8hnCY8XSrkdrXMcIS_5tBsbKOXfz49yb1yWBaOwWXDtTQesPDfesdQYMQ8WNKuDo_Fj3-EaZTwWiYa619bn4ACdIoI0YtWge-IGbETyeEic8KXXsrKOxXCC8bTwmRUmACTZc5e8XX9zsSNLxjffbZJMyXUAFU1AS8tc6kPrknD6aAP6P-c84D-bfS5rC7-YVs-YyW8-LZ8YrjWDTqWkYM2PdNISfe1yGygzoapEfHK6Np-0Q5jYTkhvsPXgsBFwEmI2CASLjj3BqkotcfrL9dPv4FNQgmffVBM__LAX8kkPglXzBfCwJ70o8-NmMoYF-TNtEwUqHnpv5ypLd05N2U66Z1aZ7__TalVxWFK_fdr8UTHw35c6HgCMlaneuIfb2zKwt3_F-v_iSlvWE8C2SS8cAm5PBnC8vYaI4ysxYgxlwJaImDg:AD-Iwly2Yj-bbmMMerQWMVG37luX0Nfa1g49j0nAYMXqDRaENAtQ75ZyY0hwst1xtYG0xC3PtqDGQVhjQX23xVC4AIuTSqG5VuFd5PRAXqXAGj487EKt3GNdj8ChbmZeIxETcQH7d_Zj4By1KB26Qzt3qQRiSbKW2cxF62_YBry3SUza",
|
"e431ace96f:0:1a665addeb00bd8f6d46cc0cc8fa39cb3932a93e737263c431435c9f80828a7f98020ecbc6240e8284dbd2f9ddce212da5c9d8d565110e278b54a50259835c76::2:AinmOs3et5j0jzNiQj31hp8Fyb_REuhr8CdFyEZuS2uU4xu9eMo9GDUHCq_DzbyqgAK0ppN9ZaE0PTT-FLRcVBZpQ25MgPw99LMT85o7nGc0l2ffsPBiOn8s6A4U551d1glzgwBkQuUNUrO1dKHd77_ZZ0uSHybxg_eVyD-FOmXkJCc1WcFhaao7Mlso0U9XV7olrRspEs75jN3gKgnEAqp3LzpB0gX_DeTJQE9eCuJ9d72oooRqrh2VcO0Bq6K8X7IUU5q5VRzJELeedeloatyRSEzpRCiXNVweCo2J9H-in6km0SaZgk0PZIJUSB5PZOrhdvw7WQONWaigD112LtcC",
|
||||||
"939576391c:0:487f993e2f270e840eaaa3d1fe82c5027ff8ee62c15c9e9867589693e8ae2f7747583863a81e8a886916d57989f6c0ab35219d605b45990d8d4682b86549f718:08a6139b7c55b402ad0ad0d8c8541581a506563f2d018636e613b7a7ba472355ea4ea13e08de962876d4b4fd5163b486dcc2be0c47a1fdecfbb54f217c1273f7:2:ATqdfUlPE7Tp9cXniiWiIZMPsjtlfWoijyfDzKGGu_OTk3btuKt6vHQMQeuswYiFaKmjqxB0hkUccdrsWTNQ-jaJAG_8aFsePJ0VjSaytfFjLNyCvNLweVfiGflJVMAWnN6otiSe0zozPMGZ-RpJJFaHV7uuNolhl_3jWPHRgcgWubLMAcxnki1xqixmzb3Ygp-9VvY6JlCEAo50sDCKX0ll3noz6RGrwe5SEyxwtsdvGJJnpLB0-2JlEjpkg2TIR-FUsP8QAMKS_zF1546vKwhTpz8FfjQOjVJ2wxIzeMxz96rLQxN5y0DIGQCSKc3E0U3u7TKtveid4qQ_2NfNhDy4bjNjdSb9AcTBzzkexWL0uu87_jy62y2AJG5xFFbsRItReM_NrLpk21lXH7xKC2pkvCavp2rX8nkl_Zprs4GjONnnrjQqUPBJABd64QpuON1P-Azv3FSAY419QXj2QmhfB-g2KxNGLXXc7gXSjWr58EY_BPP7fKuQxr-TDmkgq-QAdzAibEoJS0swK1yYDoHJeDHgG8-BrRRivHxAkGW0sYiW8ovNg92RPfMelU9SLpgw-mC85hkL5y2iUwR15EmiCkF_REkPpNUmAg:ACuwxcroXHk3FPGSQyOixwz_MFtzk1cy6TEqeT-2pQ9m5fd9AIf4xAEOKHo5hsuXNkaE42deXntrm6Kzde2uNTXYAfkXNAQL_j5oJu_9GROif3v_ZwzSTSMkEMhu-FwXGaUZR3gIST_2Sddt1Psakivczd6toHcBfFBSNIa7mcxzPj7m",
|
"3596edb275:0:b3a8e692955d9d2fe216e144dd91b467878d91d5d308f97b72f100b5b3f73501406f77fc00a71b7814bd4fa7b3f3e161cf55c8c50400a239a062a4b5da56f11c::2:AyDEvg6plLbzsM1pNExvF7HIeOwVSfzMpE9rqX5Bs9tOW5NNcyEb6xkYlOKIbYNTgAL2hGWLBN4pUSV9x6uDDolgtQRR84dUEmDTFynxp5lYPLo8viOqNgdrWilkx_H2hZSoStSrdecGAXIAsEbE_4NHZqA05yqiLnG_WvMxBE4s3NYVwttcVNCL2-GNQ019_cEXENeKM_0KgXY5f4WubbEL-mJzTkvjAnA8MhAbGnk3DkzdQ8syiqvgtuZGNeQ_vVyhqVJ1W7FrI-KeXB0o4dIfRQNLEue7YhSg7MPdFkaaqfe5pqHXHIbu-upYn3RNJsL7WTzjQIycW1r3v2NXlAwM",
|
||||||
"0d7224be02:0:31c616d4ba918e1a59a7efea6633a51ddbb94056ef5576fe61268ca0c0f3035a3a1bb4200c737c6e4a87bf979f9767f230066bb41e0641210d5676493845ddb5:1868a4cd394d0e5b0904dff18094333893ebeaf29561391ff442235fc6d0406f8ff119a8ce948b417a1fdf5067d8649e481fdfa67d85e865726a960d941dea5e:2:AcJ0WaHJyhDYold1aIwTqxJOwV-a_IJSqsQS-1x-YxOdBQFsQEEu23A1WlyqY3z0_KdEvJ8vG8NItpa53H4AuYIpAASlWlJG1KxCnWj4_5W3Xzj3eVu_gyoEgs46vQkCsSk6IqQzEs9orPwbUq3Wajwbd9O5D_CSdkYCa-chB1SZFIoPAGrzJc99-zdAQayev3j4WkSlr9jHvnYWdUeei6AE49VKXbg78BuioWkeH7FRCI2PoFH1WZnvFQoEzLJSotefchGGAG3RrxQJXbxzEKoFaLTAEuFYd5OkwUT3nKRrTuHgc4vaYEas5fmylqpmeozJMSzNfcmMKE-Kpr34xN-4Wn5kpTsdAdrCG0okMPhveRz0qdjLe7D_Rb10zAZoed3M5N99kIxDwUNVyFkMPAv4Y3gl0k_Kcg761GA5Sf8UxEFLLdKEJDD6AVMq9_18YBxe3fu96VvMpriK3sPKN2ICUZDauy9WvT35okNxp_RibID6FtipShYRHhz8tsqA2HUOUqbzcWEMqasyWritiqy8wpelBCg2nCHYWorR64Af0x9naZyNaqYuuEQ2Q6cjeoS1odAi--5QUbf8d1eQs83IDX1XUufb8qT3BQ:AGwwFjs3XifAex32HyTeSwzdjUYiCoQhM3GrRPStTI1Oxxq0nU4bPxCcvsM4xzzIIw0o9bcQkJDGBqaH_L_eWu7dAEFI_WCuaI7nS-5sLjtAQMeaQbeAy4omS5XuODVHBHadgHEQwZ2xroQgqH_3Wy7E2jphUqSWqro7cLdRbSZuxkL2",
|
"bb94fbb14c:0:f44e527f3776173dd8e7106f458dfb1acf680f5555d9cbe48de4daed8b3d0a4b692c3a5af67bfbfc5b275df8c6d27f2c647f529d3e1d33a0b52f5f800a5bb44e::2:Axt5NjbXaCznLoTQ6FZ2KB0WF9GSerApl94XXb3myLopi273EjLTKfzoqRe_1dhBnQOpn1RKKA27WY9WFU8vLfmxyEitLUhy8_8R86qQHwW2YIXz9OGlO4PQ2vDyzFEP28AdqQDSi3_ZbE68cTY4FdOtLYhmelvX6tzUXc6Oc0b2Fr_3DCBMVxLJwbuP2HzH-k3yGTa2jn2xBtb40jCQoS3CbQf5Pa7UW6elOmsL2KP4iupVq_FQk4fXZKXlVGjru6UAQE686KnMuzSc0TEeLG360JggmZhsxUN72xM6ei88L_BTu6srMGe3K0QLE_K1eFUXWat7WQ8Bim5G6FSMv1UD",
|
||||||
"8c3ba1296c:0:6137b816948f56158455356514eb396ae7fe14846af5b237003c394a38e1347bb159fffed778b958734eb3a3e43aef56c03a908af08f32d817fd8fd57be0b1b4:8048d42cedc04bc6e6fe0f358200239f4db1a5f16f4d156354ac55448fb6d5444131ec66ce53012765aefd98a45d12dbcf39e572ed83e370e0ba6595cb2252e5:2:AK0oHAiihtJ0d4YgirwblDtM9y4AFbUnMu6PcS9k6wBBCLnB5YlmbM3Ejx0MY-T1V99nmKydwmfvxdNZudDQ90qrABhXTfrDWBY1XVJmNHxQvvBRXyrlBn5ZM662JLI_qyBMo9VsdKKeSQHX4YFcu7n0mCCIDGH_jbVIyXqjLJDscoriAP8JKjOGN-XL7YizUxewM3nsaJCDED8vs0Y_iFh9owmf3KKJ2BPXlNv4hWExVoK4YAG0OjDERJoO_Y6LCnxO89xVAOyFR0PH_83mUN8I_DX8KVHiPM_Od7bgQnkB9Di3QvIzzqX8NKEEnJfOTuEUnptqmXeSih01e9KiJsLAdViU3VxxAcDoO6JOUkmGspZqPF2ms9FrKH1xRpx0EMPsFv1YJcAgBxxjvNaG-FBT8xWzpNRwBg13-XiDzRdOHNy4T60APXtnALW7J6zoXwhnQSM5XyBb54uO2IsZSeO2HEzFnEttGlKMEZTXSHfEmG4x8teYyIPC9glom-5FVWBxgu6p7xAkc6uktCKigNzHDiyxaF3m6b_oWRsSIe4FbeZv0IqidkbxU2fUFnDVfIuq5vp5knyzawnfv91pne6oWdQzQPIW1wD4Dw:AIBIHwGOEpJTwZVxMElofxxUlTOgNVnixX6mPE92OdIKOVGd87jiTBaRx66LjSIyRS5GiFk54F7XSXlQSx7eRfoDAR6whTQKxhFOh8XGfO_0bRqXyna2BAWTVFoKNcS8TmpPw2Ug-IOMl5USI92lF53P-yDPi_QouJu50uZ4SV4_bScC"
|
"91174e9989:0:d268c8b9856a8051f0794f4c221a08fb9764418187d28af7c205d89e9facc924c633af7f63cc3878bea5aefde175e16e068e93efd00f7f241c0aa7495cdd00ae::2:A5hywCOYFiGs_r347-0A9oAArsrWHwn8Zpy1AXRh4ZrVhLEH0Zzle6qek0WC3982oQMMY6ljD5DpLWwoqZwSGZDRpRIOIGutEyR-y-mvwv-nr970G7AlC7vTmCq-GGvSiDhL1S6og_JpHtGVpj6gEaTirIcG27503PhKwnZ93x8FLN-wFVX9TamePGGA82A3rkuuC90qI8bdpsAYNDZzGbXSuCSJLEaXFRj0efJn0l-No7_NnNGdBce6IYuV2jYbNxUV7AqUlhWEw8WWZl5OX9VOvskI6DGtZ13NMWc6q-1OSf8jqThdh1PeDFVNYy1qX5AI88GHQfIs54GIT8FAJeAG"
|
||||||
];
|
];
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn marshal_unmarshal_sign_verify_agree() {
|
fn marshal_unmarshal_sign_verify_agree() {
|
||||||
let mut good_identities: Vec<Identity> = Vec::with_capacity(10);
|
let gen = Identity::generate();
|
||||||
|
assert!(gen.agree(&gen).is_some());
|
||||||
|
assert!(gen.validate_identity());
|
||||||
|
let bytes = gen.to_bytes(IDENTITY_ALGORITHM_ALL, true);
|
||||||
|
let string = gen.to_string_with_options(IDENTITY_ALGORITHM_ALL, true);
|
||||||
|
assert!(Identity::from_str(string.as_str()).unwrap().eq(&gen));
|
||||||
|
let mut cursor = 0_usize;
|
||||||
|
assert!(Identity::unmarshal(&bytes, &mut cursor).unwrap().eq(&gen));
|
||||||
|
cursor = 0;
|
||||||
|
assert!(Identity::unmarshal(&bytes, &mut cursor).unwrap().secret.is_some());
|
||||||
|
assert!(Identity::from_str(string.as_str()).unwrap().secret.is_some());
|
||||||
|
|
||||||
|
let gen2 = Identity::generate();
|
||||||
|
assert!(gen2.validate_identity());
|
||||||
|
assert!(gen2.agree(&gen).unwrap().eq(&gen.agree(&gen2).unwrap()));
|
||||||
|
|
||||||
for id_str in GOOD_V0_IDENTITIES {
|
for id_str in GOOD_V0_IDENTITIES {
|
||||||
let id = Identity::from_str(id_str).unwrap();
|
let id = Identity::from_str(id_str).unwrap();
|
||||||
assert!(id.validate_identity());
|
assert!(id.validate_identity());
|
||||||
assert!(id.p521.is_none());
|
assert!(id.p384.is_none());
|
||||||
let idb = id.to_bytes(IDENTITY_ALGORITHM_ALL, true);
|
let idb = id.to_bytes(IDENTITY_ALGORITHM_ALL, true);
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).expect("unmarshal v0 failed");
|
let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).expect("unmarshal v0 failed");
|
||||||
|
@ -844,37 +856,19 @@ mod tests {
|
||||||
assert!(id_unmarshal.secret.is_some());
|
assert!(id_unmarshal.secret.is_some());
|
||||||
let idb2 = id_unmarshal.to_bytes(IDENTITY_ALGORITHM_ALL, true);
|
let idb2 = id_unmarshal.to_bytes(IDENTITY_ALGORITHM_ALL, true);
|
||||||
assert!(idb == idb2);
|
assert!(idb == idb2);
|
||||||
let sig = id.sign(&[1, 2, 3, 4, 5], false).unwrap();
|
|
||||||
assert!(id_unmarshal.verify(&[1, 2, 3, 4, 5], sig.as_slice()));
|
|
||||||
let sig = id.sign(&[1, 2, 3, 4, 5], true).unwrap();
|
|
||||||
assert!(id_unmarshal.verify(&[1, 2, 3, 4, 5], sig.as_slice()));
|
|
||||||
assert!(Identity::from_str(id.to_string().as_str()).unwrap().eq(&id));
|
assert!(Identity::from_str(id.to_string().as_str()).unwrap().eq(&id));
|
||||||
good_identities.push(id);
|
|
||||||
}
|
}
|
||||||
for id_str in GOOD_V1_IDENTITIES {
|
for id_str in GOOD_V1_IDENTITIES {
|
||||||
let id = Identity::from_str(id_str).unwrap();
|
let id = Identity::from_str(id_str).unwrap();
|
||||||
assert!(id.validate_identity());
|
assert!(id.validate_identity());
|
||||||
assert!(id.p521.is_some());
|
assert!(id.p384.is_some());
|
||||||
let idb = id.to_bytes(IDENTITY_ALGORITHM_ALL, true);
|
let idb = id.to_bytes(IDENTITY_ALGORITHM_ALL, true);
|
||||||
let mut cursor = 0;
|
let mut cursor = 0;
|
||||||
let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).expect("unmarshal v1 failed");
|
let id_unmarshal = Identity::unmarshal(&idb, &mut cursor).expect("unmarshal v1 failed");
|
||||||
assert!(id == id_unmarshal);
|
assert!(id == id_unmarshal);
|
||||||
assert!(id_unmarshal.secret.is_some());
|
|
||||||
let idb2 = id_unmarshal.to_bytes(IDENTITY_ALGORITHM_ALL, true);
|
let idb2 = id_unmarshal.to_bytes(IDENTITY_ALGORITHM_ALL, true);
|
||||||
assert!(idb == idb2);
|
assert!(idb == idb2);
|
||||||
let sig = id.sign(&[1, 2, 3, 4, 5], false).unwrap();
|
|
||||||
assert!(id_unmarshal.verify(&[1, 2, 3, 4, 5], sig.as_slice()));
|
|
||||||
let sig = id.sign(&[1, 2, 3, 4, 5], true).unwrap();
|
|
||||||
assert!(id_unmarshal.verify(&[1, 2, 3, 4, 5], sig.as_slice()));
|
|
||||||
assert!(Identity::from_str(id.to_string().as_str()).unwrap().eq(&id));
|
assert!(Identity::from_str(id.to_string().as_str()).unwrap().eq(&id));
|
||||||
good_identities.push(id);
|
|
||||||
}
|
|
||||||
for i in 0..good_identities.len() {
|
|
||||||
for j in 0..good_identities.len() {
|
|
||||||
let k0 = good_identities.get(i).unwrap().agree(good_identities.get(j).unwrap()).unwrap();
|
|
||||||
let k1 = good_identities.get(j).unwrap().agree(good_identities.get(i).unwrap()).unwrap();
|
|
||||||
assert!(k0 == k1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -887,7 +881,6 @@ mod tests {
|
||||||
let mut duration;
|
let mut duration;
|
||||||
loop {
|
loop {
|
||||||
let _id = Identity::generate();
|
let _id = Identity::generate();
|
||||||
//println!("{}", _id.to_secret_string());
|
|
||||||
end = SystemTime::now();
|
end = SystemTime::now();
|
||||||
duration = end.duration_since(start).unwrap();
|
duration = end.duration_since(start).unwrap();
|
||||||
count += 1;
|
count += 1;
|
||||||
|
|
|
@ -16,7 +16,7 @@ use std::sync::atomic::{AtomicI64, AtomicU64, AtomicU8, Ordering};
|
||||||
use arc_swap::ArcSwapOption;
|
use arc_swap::ArcSwapOption;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
use zerotier_core_crypto::hash::{SHA384, SHA384_HASH_SIZE};
|
use zerotier_core_crypto::hash::{hmac_sha384, SHA384, SHA384_HASH_SIZE};
|
||||||
use zerotier_core_crypto::poly1305::Poly1305;
|
use zerotier_core_crypto::poly1305::Poly1305;
|
||||||
use zerotier_core_crypto::random::next_u64_secure;
|
use zerotier_core_crypto::random::next_u64_secure;
|
||||||
use zerotier_core_crypto::salsa::Salsa;
|
use zerotier_core_crypto::salsa::Salsa;
|
||||||
|
@ -78,7 +78,7 @@ pub struct Peer {
|
||||||
///
|
///
|
||||||
/// This is only used for Salsa/Poly modes.
|
/// This is only used for Salsa/Poly modes.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn salsa_derive_per_packet_key(key: &Secret<48>, header: &PacketHeader, packet_size: usize) -> Secret<48> {
|
fn salsa_derive_per_packet_key(key: &Secret<64>, header: &PacketHeader, packet_size: usize) -> Secret<64> {
|
||||||
let hb = header.as_bytes();
|
let hb = header.as_bytes();
|
||||||
let mut k = key.clone();
|
let mut k = key.clone();
|
||||||
for i in 0..18 {
|
for i in 0..18 {
|
||||||
|
@ -92,9 +92,9 @@ fn salsa_derive_per_packet_key(key: &Secret<48>, header: &PacketHeader, packet_s
|
||||||
|
|
||||||
/// Create initialized instances of Salsa20/12 and Poly1305 for a packet.
|
/// Create initialized instances of Salsa20/12 and Poly1305 for a packet.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn salsa_poly_create(secret: &SymmetricSecret, header: &PacketHeader, packet_size: usize) -> (Salsa, Poly1305) {
|
fn salsa_poly_create(secret: &SymmetricSecret, header: &PacketHeader, packet_size: usize) -> (Salsa<12>, Poly1305) {
|
||||||
let key = salsa_derive_per_packet_key(&secret.key, header, packet_size);
|
let key = salsa_derive_per_packet_key(&secret.key, header, packet_size);
|
||||||
let mut salsa = Salsa::new(&key.0[0..32], &header.id, true).unwrap();
|
let mut salsa = Salsa::<12>::new(&key.0[0..32], &header.id);
|
||||||
let mut poly1305_key = [0_u8; 32];
|
let mut poly1305_key = [0_u8; 32];
|
||||||
salsa.crypt_in_place(&mut poly1305_key);
|
salsa.crypt_in_place(&mut poly1305_key);
|
||||||
(salsa, Poly1305::new(&poly1305_key).unwrap())
|
(salsa, Poly1305::new(&poly1305_key).unwrap())
|
||||||
|
@ -266,10 +266,10 @@ impl Peer {
|
||||||
if extended_authentication {
|
if extended_authentication {
|
||||||
if payload.len() >= (1 + SHA384_HASH_SIZE) {
|
if payload.len() >= (1 + SHA384_HASH_SIZE) {
|
||||||
let actual_end_of_payload = payload.len() - SHA384_HASH_SIZE;
|
let actual_end_of_payload = payload.len() - SHA384_HASH_SIZE;
|
||||||
let hmac = SHA384::hmac_multipart(self.static_secret.packet_hmac_key.as_ref(), &[u64_as_bytes(&message_id), payload.as_bytes()]);
|
//let hmac = hmac_sha384(self.static_secret.packet_hmac_key.as_ref(), &[u64_as_bytes(&message_id), payload.as_bytes()]);
|
||||||
if !hmac.eq(&(payload.as_bytes()[actual_end_of_payload..])) {
|
//if !hmac.eq(&(payload.as_bytes()[actual_end_of_payload..])) {
|
||||||
return;
|
// return;
|
||||||
}
|
//}
|
||||||
payload.set_size(actual_end_of_payload);
|
payload.set_size(actual_end_of_payload);
|
||||||
} else {
|
} else {
|
||||||
return;
|
return;
|
||||||
|
@ -449,13 +449,13 @@ impl Peer {
|
||||||
let zero_moon_count = packet.append_bytes_fixed_get_mut::<2>().unwrap();
|
let zero_moon_count = packet.append_bytes_fixed_get_mut::<2>().unwrap();
|
||||||
let mut salsa_iv = message_id.to_ne_bytes();
|
let mut salsa_iv = message_id.to_ne_bytes();
|
||||||
salsa_iv[7] &= 0xf8;
|
salsa_iv[7] &= 0xf8;
|
||||||
Salsa::new(&self.static_secret.key.0[0..32], &salsa_iv, true).unwrap().crypt(&[0_u8, 0_u8], zero_moon_count);
|
Salsa::<12>::new(&self.static_secret.key.0[0..32], &salsa_iv).crypt(&[0_u8, 0_u8], zero_moon_count);
|
||||||
|
|
||||||
// Size of dictionary with optional fields, currently none. For future use.
|
// Size of dictionary with optional fields, currently none. For future use.
|
||||||
assert!(packet.append_u16(0).is_ok());
|
assert!(packet.append_u16(0).is_ok());
|
||||||
|
|
||||||
// Add full HMAC for strong authentication with newer nodes.
|
// Add full HMAC for strong authentication with newer nodes.
|
||||||
assert!(packet.append_bytes_fixed(&SHA384::hmac_multipart(&self.static_secret.packet_hmac_key.0, &[u64_as_bytes(&message_id), &packet.as_bytes()[PACKET_HEADER_SIZE..]])).is_ok());
|
//assert!(packet.append_bytes_fixed(&SHA384::hmac_multipart(&self.static_secret.packet_hmac_key.0, &[u64_as_bytes(&message_id), &packet.as_bytes()[PACKET_HEADER_SIZE..]])).is_ok());
|
||||||
|
|
||||||
// LEGACY: set MAC field in header with poly1305 for older nodes.
|
// LEGACY: set MAC field in header with poly1305 for older nodes.
|
||||||
// Newer nodes use the HMAC for stronger verification.
|
// Newer nodes use the HMAC for stronger verification.
|
||||||
|
|
|
@ -41,9 +41,6 @@ pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1: u8 = b'1';
|
||||||
/// KBKDF usage label for the key used to advance the ratchet.
|
/// KBKDF usage label for the key used to advance the ratchet.
|
||||||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_NEXT_KEY: u8 = b'e';
|
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_NEXT_KEY: u8 = b'e';
|
||||||
|
|
||||||
/// KBKDF usage label for generating the ratchet state ID (which is not actually a key).
|
|
||||||
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_STATE_KEY: u8 = b'E';
|
|
||||||
|
|
||||||
/// Try to re-key ephemeral keys after this time.
|
/// Try to re-key ephemeral keys after this time.
|
||||||
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 300000; // 5 minutes
|
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 300000; // 5 minutes
|
||||||
|
|
||||||
|
@ -179,7 +176,7 @@ pub const IDENTITY_V0_POW_THRESHOLD: u8 = 17;
|
||||||
|
|
||||||
/// Proof of work difficulty (threshold) for new v1 identities.
|
/// Proof of work difficulty (threshold) for new v1 identities.
|
||||||
/// This is lower than the V0 threshold, causing the V0 part of V1 identities to verify on old nodes.
|
/// This is lower than the V0 threshold, causing the V0 part of V1 identities to verify on old nodes.
|
||||||
pub const IDENTITY_V1_POW_THRESHOLD: u8 = 9;
|
pub const IDENTITY_V1_POW_THRESHOLD: u8 = 13;
|
||||||
|
|
||||||
/// Compress a packet and return true if compressed.
|
/// Compress a packet and return true if compressed.
|
||||||
/// The 'dest' buffer must be empty (will panic otherwise). A return value of false indicates an error or
|
/// The 'dest' buffer must be empty (will panic otherwise). A return value of false indicates an error or
|
||||||
|
|
|
@ -7,15 +7,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use zerotier_core_crypto::aes_gmac_siv::AesGmacSiv;
|
use zerotier_core_crypto::aes_gmac_siv::AesGmacSiv;
|
||||||
use zerotier_core_crypto::hash::SHA384_HASH_SIZE;
|
use zerotier_core_crypto::kbkdf::*;
|
||||||
use zerotier_core_crypto::kbkdf::zt_kbkdf_hmac_sha384;
|
|
||||||
use zerotier_core_crypto::secret::Secret;
|
use zerotier_core_crypto::secret::Secret;
|
||||||
|
|
||||||
use crate::util::pool::{Pool, PoolFactory};
|
use crate::util::pool::{Pool, PoolFactory};
|
||||||
use crate::vl1::protocol::*;
|
use crate::vl1::protocol::*;
|
||||||
|
|
||||||
/// Pool of reusable AES-GMAC-SIV instances.
|
/// Pool of reusable AES-GMAC-SIV instances.
|
||||||
pub(crate) struct AesGmacSivPoolFactory(Secret<SHA384_HASH_SIZE>, Secret<SHA384_HASH_SIZE>);
|
pub(crate) struct AesGmacSivPoolFactory(Secret<48>, Secret<48>);
|
||||||
|
|
||||||
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -30,16 +29,10 @@ impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
|
||||||
/// This contains the key and several sub-keys and ciphers keyed with sub-keys.
|
/// This contains the key and several sub-keys and ciphers keyed with sub-keys.
|
||||||
pub(crate) struct SymmetricSecret {
|
pub(crate) struct SymmetricSecret {
|
||||||
/// The root shared symmetric secret from which other keys are derived.
|
/// The root shared symmetric secret from which other keys are derived.
|
||||||
pub key: Secret<SHA384_HASH_SIZE>,
|
pub key: Secret<64>,
|
||||||
|
|
||||||
/// Key for adding an HMAC to packets e.g. in v2+ HELLO.
|
/// Key for adding an HMAC to packets e.g. in v2+ HELLO.
|
||||||
pub packet_hmac_key: Secret<SHA384_HASH_SIZE>,
|
pub packet_hmac_key: Secret<64>,
|
||||||
|
|
||||||
/// A key used as input to the ephemeral key ratcheting mechanism.
|
|
||||||
pub ephemeral_ratchet_key: Secret<SHA384_HASH_SIZE>,
|
|
||||||
|
|
||||||
/// A key used to verify the state of the ephemeral ratchet.
|
|
||||||
pub ephemeral_ratchet_state_key: Secret<SHA384_HASH_SIZE>,
|
|
||||||
|
|
||||||
/// A pool of reusable keyed and initialized AES-GMAC-SIV ciphers.
|
/// A pool of reusable keyed and initialized AES-GMAC-SIV ciphers.
|
||||||
pub aes_gmac_siv: Pool<AesGmacSiv, AesGmacSivPoolFactory>,
|
pub aes_gmac_siv: Pool<AesGmacSiv, AesGmacSivPoolFactory>,
|
||||||
|
@ -54,18 +47,14 @@ impl Eq for SymmetricSecret {}
|
||||||
|
|
||||||
impl SymmetricSecret {
|
impl SymmetricSecret {
|
||||||
/// Create a new symmetric secret, deriving all sub-keys and such.
|
/// Create a new symmetric secret, deriving all sub-keys and such.
|
||||||
pub fn new(base_key: Secret<SHA384_HASH_SIZE>) -> SymmetricSecret {
|
pub fn new(base_key: Secret<64>) -> SymmetricSecret {
|
||||||
let usage_packet_hmac = zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC, 0, 0);
|
let usage_packet_hmac = zt_kbkdf_hmac_sha512(&base_key.0, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC, 0, 0);
|
||||||
let usage_ephemeral_ratchet = zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_NEXT_KEY, 0, 0);
|
|
||||||
let usage_ephemeral_ratchet_state = zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_EPHEMERAL_RATCHET_STATE_KEY, 0, 0);
|
|
||||||
let aes_factory = AesGmacSivPoolFactory(
|
let aes_factory = AesGmacSivPoolFactory(
|
||||||
zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, 0, 0),
|
zt_kbkdf_hmac_sha384(&base_key.0[0..48], KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, 0, 0),
|
||||||
zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, 0, 0));
|
zt_kbkdf_hmac_sha384(&base_key.0[0..48], KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, 0, 0));
|
||||||
SymmetricSecret {
|
SymmetricSecret {
|
||||||
key: base_key,
|
key: base_key,
|
||||||
packet_hmac_key: usage_packet_hmac,
|
packet_hmac_key: usage_packet_hmac,
|
||||||
ephemeral_ratchet_key: usage_ephemeral_ratchet,
|
|
||||||
ephemeral_ratchet_state_key: usage_ephemeral_ratchet_state,
|
|
||||||
aes_gmac_siv: Pool::new(2, aes_factory),
|
aes_gmac_siv: Pool::new(2, aes_factory),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
121
zerotier-system-service/Cargo.lock
generated
121
zerotier-system-service/Cargo.lock
generated
|
@ -40,7 +40,6 @@ dependencies = [
|
||||||
name = "aes-gmac-siv"
|
name = "aes-gmac-siv"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gcrypt",
|
|
||||||
"openssl",
|
"openssl",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -230,7 +229,7 @@ checksum = "b21b63ab5a0db0369deb913540af2892750e42d949faacc7a61495ac418a1692"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-io",
|
"async-io",
|
||||||
"blocking",
|
"blocking",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"event-listener",
|
"event-listener",
|
||||||
"futures-lite",
|
"futures-lite",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -433,12 +432,6 @@ version = "1.0.71"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
|
checksum = "79c2681d6594606957bbb8631c4b90a7fcaaa72cdb714743a437b156d6a7eedd"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "0.1.10"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
version = "1.0.0"
|
version = "1.0.0"
|
||||||
|
@ -560,7 +553,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70"
|
checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal 1.0.0",
|
"bare-metal 1.0.0",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"riscv",
|
"riscv",
|
||||||
]
|
]
|
||||||
|
@ -571,7 +564,7 @@ version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9"
|
checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -581,7 +574,7 @@ version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
|
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -595,16 +588,6 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cstr-argument"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6bd9c8e659a473bce955ae5c35b116af38af11a7acb0b480e01f3ed348aeb40"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if 1.0.0",
|
|
||||||
"memchr",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctor"
|
name = "ctor"
|
||||||
version = "0.1.21"
|
version = "0.1.21"
|
||||||
|
@ -643,7 +626,7 @@ version = "4.0.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
|
checksum = "e77a43b28d0668df09411cb0bc9a8c2adc40f9a048afe863e05fd43251e8e39c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -820,20 +803,6 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gcrypt"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c2ee79dcb8915fc0e9d8364e87d2215555076aa159d0a5d84ba9dba109b0d59"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"cstr-argument",
|
|
||||||
"gpg-error",
|
|
||||||
"libc",
|
|
||||||
"libgcrypt-sys",
|
|
||||||
"once_cell",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generic-array"
|
name = "generic-array"
|
||||||
version = "0.14.4"
|
version = "0.14.4"
|
||||||
|
@ -850,7 +819,7 @@ version = "0.1.16"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
@ -861,7 +830,7 @@ version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"wasi 0.10.2+wasi-snapshot-preview1",
|
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||||
]
|
]
|
||||||
|
@ -889,15 +858,6 @@ dependencies = [
|
||||||
"web-sys",
|
"web-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "gpg-error"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7073b9ac823434ae73608715086e944d694a7ce2677371b8c5253300d1f767f1"
|
|
||||||
dependencies = [
|
|
||||||
"libgpg-error-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hash32"
|
name = "hash32"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -967,7 +927,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ea880b03c18a7e981d7fb3608b8904a98425d53c440758fcebf7d934aa56547c"
|
checksum = "ea880b03c18a7e981d7fb3608b8904a98425d53c440758fcebf7d934aa56547c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"dashmap",
|
"dashmap",
|
||||||
"http-types",
|
"http-types",
|
||||||
"log",
|
"log",
|
||||||
|
@ -1024,7 +984,7 @@ version = "0.1.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
|
checksum = "716d3d89f35ac6a34fd0eed635395f4c3b76fa889338a4632e5231a8684216bd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1063,28 +1023,6 @@ version = "0.2.103"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
checksum = "dd8f7255a17a627354f321ef0055d63b898c6fb27eff628af4d1b66b7331edf6"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libgcrypt-sys"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "62eb5d6d9cd6d8c8adf9641c95b223eb14f07a7a81c082e2d08f0bf3880214e4"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"cfg-if 0.1.10",
|
|
||||||
"libc",
|
|
||||||
"libgpg-error-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libgpg-error-sys"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ffb1aedf0efc5d25fdd08eb52b0759c71d02ac77fd1879b96e95211239528897"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"winreg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "lock_api"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
@ -1100,7 +1038,7 @@ version = "0.4.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"value-bag",
|
"value-bag",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1208,7 +1146,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a"
|
checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"foreign-types",
|
"foreign-types",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
|
@ -1251,7 +1189,7 @@ version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
checksum = "d76e8e1493bcac0d2766c42737f34458f1c8c50c0d23bcb24ea953affb273216"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"instant",
|
"instant",
|
||||||
"libc",
|
"libc",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
|
@ -1315,13 +1253,24 @@ version = "2.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25"
|
checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"libc",
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"wepoll-ffi",
|
"wepoll-ffi",
|
||||||
"winapi",
|
"winapi",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "poly1305"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "048aeb476be11a4b6ca432ca569e375810de9294ae78f4774e78ea98a9246ede"
|
||||||
|
dependencies = [
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
"universal-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polyval"
|
name = "polyval"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
|
@ -1606,7 +1555,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
|
checksum = "b69f9a4c9740d74c5baa3fd2e547f9525fa8088a8a958e0ca2409a514e33f5fa"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer",
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"digest",
|
"digest",
|
||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
|
@ -1936,7 +1885,7 @@ version = "1.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1f559b464de2e2bdabcac6a210d12e9b5a5973c251e102c44c585c71d51bd78e"
|
checksum = "1f559b464de2e2bdabcac6a210d12e9b5a5973c251e102c44c585c71d51bd78e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"static_assertions",
|
"static_assertions",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2070,7 +2019,7 @@ version = "0.2.78"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
|
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"wasm-bindgen-macro",
|
"wasm-bindgen-macro",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2095,7 +2044,7 @@ version = "0.4.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
|
checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"wasm-bindgen",
|
"wasm-bindgen",
|
||||||
"web-sys",
|
"web-sys",
|
||||||
|
@ -2171,15 +2120,6 @@ version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winreg"
|
|
||||||
version = "0.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69"
|
|
||||||
dependencies = [
|
|
||||||
"winapi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x25519-dalek"
|
name = "x25519-dalek"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -2218,8 +2158,11 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gmac-siv",
|
"aes-gmac-siv",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"gcrypt",
|
"foreign-types",
|
||||||
"heapless",
|
"heapless",
|
||||||
|
"lazy_static",
|
||||||
|
"openssl",
|
||||||
|
"poly1305",
|
||||||
"rand_core 0.5.1",
|
"rand_core 0.5.1",
|
||||||
"subtle",
|
"subtle",
|
||||||
"x25519-dalek",
|
"x25519-dalek",
|
||||||
|
|
Loading…
Add table
Reference in a new issue