mirror of
https://github.com/signalapp/libsignal.git
synced 2024-09-20 03:52:17 +02:00
Client-side rust-only HSM enclave library.
This commit is contained in:
parent
0d5d70038d
commit
9aa79c0c59
5
.github/workflows/build_and_test.yml
vendored
5
.github/workflows/build_and_test.yml
vendored
@ -131,10 +131,7 @@ jobs:
|
|||||||
target: i686-unknown-linux-gnu
|
target: i686-unknown-linux-gnu
|
||||||
|
|
||||||
- name: Check for duplicate dependencies
|
- name: Check for duplicate dependencies
|
||||||
run: |
|
run: ./bin/verify_duplicate_crates
|
||||||
DUPS="$(cargo tree -d -e normal --workspace)"
|
|
||||||
echo "$DUPS"
|
|
||||||
test -z "$DUPS"
|
|
||||||
|
|
||||||
- name: Rustfmt check
|
- name: Rustfmt check
|
||||||
run: cargo fmt --all -- --check
|
run: cargo fmt --all -- --check
|
||||||
|
173
Cargo.lock
generated
173
Cargo.lock
generated
@ -39,6 +39,20 @@ dependencies = [
|
|||||||
"opaque-debug",
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aes-gcm"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bc3be92e19a7ef47457b8e6f90707e12b6ac5d20c6f3866584fa3be0787d839f"
|
||||||
|
dependencies = [
|
||||||
|
"aead",
|
||||||
|
"aes",
|
||||||
|
"cipher",
|
||||||
|
"ctr",
|
||||||
|
"ghash",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "aes-gcm-siv"
|
name = "aes-gcm-siv"
|
||||||
version = "0.10.1"
|
version = "0.10.1"
|
||||||
@ -135,6 +149,17 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "blake2"
|
||||||
|
version = "0.9.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a4e37d16930f5459780f5621038b6382b9bb37c19016f39fb6b5808d831f174"
|
||||||
|
dependencies = [
|
||||||
|
"crypto-mac 0.8.0",
|
||||||
|
"digest",
|
||||||
|
"opaque-debug",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
@ -197,7 +222,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 = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
|
checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustc_version",
|
"rustc_version 0.2.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -224,6 +249,31 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chacha20"
|
||||||
|
version = "0.7.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ea8756167ea0aca10e066cdbe7813bd71d2f24e69b0bc7b50509590cef2ce0b9"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if 1.0.0",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "chacha20poly1305"
|
||||||
|
version = "0.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b6547abe025f4027edacd9edaa357aded014eecec42a5070d9b885c3c334aba2"
|
||||||
|
dependencies = [
|
||||||
|
"aead",
|
||||||
|
"chacha20",
|
||||||
|
"cipher",
|
||||||
|
"poly1305",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "chrono"
|
name = "chrono"
|
||||||
version = "0.4.19"
|
version = "0.4.19"
|
||||||
@ -365,6 +415,16 @@ dependencies = [
|
|||||||
"loom",
|
"loom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto-mac"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"subtle",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crypto-mac"
|
name = "crypto-mac"
|
||||||
version = "0.9.1"
|
version = "0.9.1"
|
||||||
@ -415,7 +475,7 @@ dependencies = [
|
|||||||
[[package]]
|
[[package]]
|
||||||
name = "curve25519-dalek"
|
name = "curve25519-dalek"
|
||||||
version = "3.0.0"
|
version = "3.0.0"
|
||||||
source = "git+https://github.com/signalapp/curve25519-dalek?branch=3.0.0-lizard2#2694ad3b789635f90f941648ae952f58d59ffc73"
|
source = "git+https://github.com/signalapp/curve25519-dalek.git?branch=3.0.0-lizard2#2694ad3b789635f90f941648ae952f58d59ffc73"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"digest",
|
"digest",
|
||||||
@ -626,10 +686,21 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff"
|
checksum = "deae6d9dbb35ec2c502d62b8f7b1c000a0822c3b0794ba36b3149c0a1c840dff"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"crypto-mac",
|
"crypto-mac 0.9.1",
|
||||||
"digest",
|
"digest",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hsm-enclave"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aes-gcm",
|
||||||
|
"rand_core 0.6.2",
|
||||||
|
"sha2",
|
||||||
|
"snow",
|
||||||
|
"x25519-dalek",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "1.6.1"
|
version = "1.6.1"
|
||||||
@ -920,29 +991,29 @@ checksum = "1255076139a83bb467426e7f8d0134968a8118844faa755985e077cf31850333"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "neon"
|
name = "neon"
|
||||||
version = "0.9.0"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "158d44fdd9cc93d5051c15e970727e94e4440c3c585c77b2f482df397a661fcf"
|
checksum = "5e85820b585bf3360bf158ac87a75764c48e361c91bbeb69873e6613cc78c023"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cslice",
|
"cslice",
|
||||||
"neon-build",
|
"neon-build",
|
||||||
"neon-macros",
|
"neon-macros",
|
||||||
"neon-runtime",
|
"neon-runtime",
|
||||||
"semver",
|
"semver 0.9.0",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "neon-build"
|
name = "neon-build"
|
||||||
version = "0.9.0"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "067f30e1bb8bec9a0f2be115fd54430dd66472872c7c1491473618a405c1daac"
|
checksum = "ad9febc63f515156d4311a0c43899d3ace46352ecdd591c21b98ca3974f2a0d0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "neon-macros"
|
name = "neon-macros"
|
||||||
version = "0.9.0"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "72adff7fd8a79dc3a18e565086179fa7608c555dd0e553e4eafebf9033b6a01c"
|
checksum = "987f12c91eb6ce0b67819f7c5fb4d391de64cf411c605ed027f03507a33943b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
@ -950,9 +1021,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "neon-runtime"
|
name = "neon-runtime"
|
||||||
version = "0.9.0"
|
version = "0.9.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "be066c504047007b93709dc164c42f9f19c2ff365e7bf71240c8f88c92f2e687"
|
checksum = "02662cd2e62b131937bdef85d0918b05bc3c204daf4c64af62845403eccb60f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if 1.0.0",
|
"cfg-if 1.0.0",
|
||||||
"libloading",
|
"libloading",
|
||||||
@ -1137,6 +1208,15 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pest"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "10f4872ae94d7b90ae48754df22fd42ad52ce740b8f370b03da4835417403e53"
|
||||||
|
dependencies = [
|
||||||
|
"ucd-trie",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "petgraph"
|
name = "petgraph"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@ -1261,6 +1341,17 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "poly1305"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9fcffab1f78ebbdf4b93b68c1ffebc24037eedf271edaca795732b24e5e4e349"
|
||||||
|
dependencies = [
|
||||||
|
"cpufeatures",
|
||||||
|
"opaque-debug",
|
||||||
|
"universal-hash",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "polyval"
|
name = "polyval"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@ -1554,7 +1645,16 @@ 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 = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver",
|
"semver 0.9.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee"
|
||||||
|
dependencies = [
|
||||||
|
"semver 0.11.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1596,7 +1696,16 @@ version = "0.9.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"semver-parser",
|
"semver-parser 0.7.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "0.11.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6"
|
||||||
|
dependencies = [
|
||||||
|
"semver-parser 0.10.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -1605,6 +1714,15 @@ version = "0.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver-parser"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7"
|
||||||
|
dependencies = [
|
||||||
|
"pest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.123"
|
version = "1.0.123"
|
||||||
@ -1752,6 +1870,23 @@ 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 = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "snow"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6142f7c25e94f6fd25a32c3348ec230df9109b463f59c8c7acc4bd34936babb7"
|
||||||
|
dependencies = [
|
||||||
|
"aes-gcm",
|
||||||
|
"blake2",
|
||||||
|
"chacha20poly1305",
|
||||||
|
"rand 0.8.3",
|
||||||
|
"rand_core 0.6.2",
|
||||||
|
"rustc_version 0.3.3",
|
||||||
|
"sha2",
|
||||||
|
"subtle",
|
||||||
|
"x25519-dalek",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
version = "0.5.2"
|
version = "0.5.2"
|
||||||
@ -1891,6 +2026,12 @@ version = "1.12.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ucd-trie"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-segmentation"
|
name = "unicode-segmentation"
|
||||||
version = "1.7.1"
|
version = "1.7.1"
|
||||||
@ -2084,9 +2225,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "zeroize"
|
||||||
version = "1.2.0"
|
version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81a974bcdd357f0dca4d41677db03436324d45a4c9ed2d0b873a5a360ce41c36"
|
checksum = "4756f7db3f7b5574938c3eb1c117038b8e07f95ee6718c0efad4ac21508f1efd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zeroize_derive",
|
"zeroize_derive",
|
||||||
]
|
]
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
members = [
|
members = [
|
||||||
"rust/crypto",
|
"rust/crypto",
|
||||||
"rust/device-transfer",
|
"rust/device-transfer",
|
||||||
|
"rust/hsm-enclave",
|
||||||
"rust/poksho",
|
"rust/poksho",
|
||||||
"rust/protocol",
|
"rust/protocol",
|
||||||
"rust/bridge/ffi",
|
"rust/bridge/ffi",
|
||||||
@ -14,6 +15,7 @@ default-members = [
|
|||||||
"rust/poksho",
|
"rust/poksho",
|
||||||
"rust/protocol",
|
"rust/protocol",
|
||||||
]
|
]
|
||||||
|
resolver = "2" # so that our dev-dependency features don't leak into products
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
curve25519-dalek = { git = 'https://github.com/signalapp/curve25519-dalek', branch = '3.0.0-lizard2' }
|
curve25519-dalek = { git = 'https://github.com/signalapp/curve25519-dalek', branch = '3.0.0-lizard2' }
|
||||||
|
32
bin/verify_duplicate_crates
Executable file
32
bin/verify_duplicate_crates
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright 2021 Signal Messenger, LLC.
|
||||||
|
# SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
#
|
||||||
|
|
||||||
|
# For now, these dependencies have conflicting requirements.
|
||||||
|
# You can use the `cargo tree` command below to see where they come from,
|
||||||
|
# and then document them here.
|
||||||
|
#
|
||||||
|
# getrandom + rand_core:
|
||||||
|
# curve25519-dalek + x25519-dalek use rand_core 0.5, snow uses rand_core 0.6
|
||||||
|
# serde:
|
||||||
|
# num_enum_derive indirectly uses serde in a proc-macro;
|
||||||
|
# unfortunately that shows up as a repeat here
|
||||||
|
EXPECTED="\
|
||||||
|
getrandom v0.1.16
|
||||||
|
|
||||||
|
getrandom v0.2.2
|
||||||
|
|
||||||
|
rand_core v0.5.1
|
||||||
|
|
||||||
|
rand_core v0.6.2
|
||||||
|
|
||||||
|
serde v1.0.123
|
||||||
|
|
||||||
|
serde v1.0.123"
|
||||||
|
|
||||||
|
if [[ $(cargo tree -d -e normal --workspace --depth 0) != "${EXPECTED}" ]]; then
|
||||||
|
cargo tree -d -e normal --workspace
|
||||||
|
fi
|
@ -47,3 +47,4 @@ extra_bindings = ["libsignal-bridge"]
|
|||||||
|
|
||||||
[parse.expand]
|
[parse.expand]
|
||||||
crates = ["libsignal-ffi", "libsignal-bridge"]
|
crates = ["libsignal-ffi", "libsignal-bridge"]
|
||||||
|
features = ["libsignal-bridge/ffi"]
|
||||||
|
@ -21,3 +21,4 @@ extra_bindings = ["libsignal-bridge"]
|
|||||||
|
|
||||||
[parse.expand]
|
[parse.expand]
|
||||||
crates = ["libsignal-jni", "libsignal-bridge"]
|
crates = ["libsignal-jni", "libsignal-bridge"]
|
||||||
|
features = ["libsignal-bridge/jni"]
|
||||||
|
15
rust/hsm-enclave/Cargo.toml
Normal file
15
rust/hsm-enclave/Cargo.toml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "hsm-enclave"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Graeme Connell <gram@signal.org>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
snow = { version = "0.8.0", default-features = false }
|
||||||
|
aes-gcm = "0.9"
|
||||||
|
rand_core = { version = "0.6", features = ["getrandom"] }
|
||||||
|
sha2 = "0.9"
|
||||||
|
x25519-dalek = "1.1"
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
snow = { version = "0.8.0", features = ["default-resolver"] }
|
160
rust/hsm-enclave/src/lib.rs
Normal file
160
rust/hsm-enclave/src/lib.rs
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 Signal Messenger, LLC.
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
//
|
||||||
|
|
||||||
|
//! Support logic for Signal's device-to-device transfer feature.
|
||||||
|
|
||||||
|
#![deny(unsafe_code)]
|
||||||
|
#![warn(missing_docs)]
|
||||||
|
|
||||||
|
use std::convert::From;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
mod snow_resolver;
|
||||||
|
|
||||||
|
/// Error types for device transfer.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
/// Failure to connect to a trusted HSM.
|
||||||
|
HSMCommunicationError(snow::Error),
|
||||||
|
/// Failure to connect to trusted code on the given HSM.
|
||||||
|
TrustedCodeError,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Error::HSMCommunicationError(n) => write!(f, "Error in Noise protocol ({})", n),
|
||||||
|
Error::TrustedCodeError => {
|
||||||
|
write!(f, "Trusted HSM process does not match trusted code hash")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<snow::Error> for Error {
|
||||||
|
fn from(e: snow::Error) -> Self {
|
||||||
|
Error::HSMCommunicationError(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps a connection handshake to an HSM-resident enclave.
|
||||||
|
///
|
||||||
|
/// ```pseudocode
|
||||||
|
/// let mut client_conn_establishment = ClientConnectionEstablishment::new(...)?;
|
||||||
|
/// let websocket = ... open websocket ...
|
||||||
|
/// websocket.send(client_conn_establishment.initial_request());
|
||||||
|
/// let initial_response = websocket.recv(...);
|
||||||
|
/// let conn = client_conn_establishment.complete(initial_response)?;
|
||||||
|
/// ```
|
||||||
|
pub struct ClientConnectionEstablishment {
|
||||||
|
hs: snow::HandshakeState,
|
||||||
|
initial_message: Vec<u8>,
|
||||||
|
trusted_code_hashes: Vec<[u8; CODE_HASH_SIZE]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
const NOISE_PATTERN: &str = "Noise_NK_25519_AESGCM_SHA256";
|
||||||
|
const NOISE_HANDSHAKE_OVERHEAD: usize = 64; // TODO: this could be more exact
|
||||||
|
|
||||||
|
/// The size in bytes of a code hash.
|
||||||
|
pub const CODE_HASH_SIZE: usize = 32;
|
||||||
|
/// The size in bytes of a public key.
|
||||||
|
pub const PUB_KEY_SIZE: usize = 32;
|
||||||
|
|
||||||
|
impl ClientConnectionEstablishment {
|
||||||
|
/// Creates a new client connection establishment.
|
||||||
|
pub fn new(
|
||||||
|
trusted_public_key: [u8; PUB_KEY_SIZE],
|
||||||
|
trusted_code_hashes: Vec<[u8; CODE_HASH_SIZE]>,
|
||||||
|
) -> Result<Self, Error> {
|
||||||
|
let mut hs = snow::Builder::with_resolver(
|
||||||
|
NOISE_PATTERN.parse().expect("valid"),
|
||||||
|
Box::new(snow_resolver::Resolver),
|
||||||
|
)
|
||||||
|
.remote_public_key(&trusted_public_key[..])
|
||||||
|
.build_initiator()?;
|
||||||
|
let payload = trusted_code_hashes.concat();
|
||||||
|
let mut initial_message = vec![0u8; NOISE_HANDSHAKE_OVERHEAD + payload.len()];
|
||||||
|
let size = hs.write_message(&payload, &mut initial_message)?;
|
||||||
|
initial_message.truncate(size);
|
||||||
|
Ok(Self {
|
||||||
|
hs,
|
||||||
|
initial_message,
|
||||||
|
trusted_code_hashes,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initial message to send to server to establish connection.
|
||||||
|
pub fn initial_request(&self) -> &[u8] {
|
||||||
|
&self.initial_message
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Completes client connection initiation, returns a valid client connection.
|
||||||
|
pub fn complete(mut self, initial_received: &[u8]) -> Result<ClientConnection, Error> {
|
||||||
|
let mut received_hash = [0u8; CODE_HASH_SIZE];
|
||||||
|
let size = self.hs.read_message(initial_received, &mut received_hash)?;
|
||||||
|
if size != received_hash.len() {
|
||||||
|
return Err(Error::TrustedCodeError);
|
||||||
|
}
|
||||||
|
if !self.trusted_code_hashes.contains(&received_hash) {
|
||||||
|
return Err(Error::TrustedCodeError);
|
||||||
|
}
|
||||||
|
let transport = self.hs.into_transport_mode()?;
|
||||||
|
Ok(ClientConnection { transport })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Wraps an established connection to an HSM-resident enclave.
|
||||||
|
///
|
||||||
|
/// ```pseudocode
|
||||||
|
/// let conn = client_connection_establishment.complete(...)?;
|
||||||
|
///
|
||||||
|
/// // any number of sends:
|
||||||
|
/// let plaintext_to_send: &[u8] = ...;
|
||||||
|
/// let encrypted_to_send: Vec<u8> = conn.send(plaintext_to_send)?;
|
||||||
|
/// websocket.send(&encrypted_to_send)?;
|
||||||
|
///
|
||||||
|
/// // and receives:
|
||||||
|
/// let encrypted_received = websocket.recv(...)?;
|
||||||
|
/// let plaintext_received: Vec<u8> = conn.recv(encrypted_received)?;
|
||||||
|
/// ```
|
||||||
|
pub struct ClientConnection {
|
||||||
|
transport: snow::TransportState,
|
||||||
|
}
|
||||||
|
|
||||||
|
const NOISE_TRANSPORT_PER_PACKET_MAX: usize = 65535;
|
||||||
|
const NOISE_TRANSPORT_PER_PAYLOAD_OVERHEAD: usize = 16;
|
||||||
|
const NOISE_TRANSPORT_PER_PAYLOAD_MAX: usize =
|
||||||
|
NOISE_TRANSPORT_PER_PACKET_MAX - NOISE_TRANSPORT_PER_PAYLOAD_OVERHEAD;
|
||||||
|
|
||||||
|
impl ClientConnection {
|
||||||
|
/// Wrap a plaintext message to be sent, returning the ciphertext.
|
||||||
|
pub fn send(&mut self, plaintext_to_send: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
|
let max_ciphertext_size = plaintext_to_send.len()
|
||||||
|
+ (1 + plaintext_to_send.len() / NOISE_TRANSPORT_PER_PAYLOAD_MAX)
|
||||||
|
* NOISE_HANDSHAKE_OVERHEAD;
|
||||||
|
let mut ciphertext = vec![0u8; max_ciphertext_size];
|
||||||
|
let mut total_size = 0;
|
||||||
|
for chunk in plaintext_to_send.chunks(NOISE_TRANSPORT_PER_PAYLOAD_MAX) {
|
||||||
|
total_size += self
|
||||||
|
.transport
|
||||||
|
.write_message(chunk, &mut ciphertext[total_size..])?;
|
||||||
|
}
|
||||||
|
ciphertext.truncate(total_size);
|
||||||
|
Ok(ciphertext)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unwrap a ciphertext message that's been received, returning the plaintext.
|
||||||
|
pub fn recv(&mut self, received_ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
|
||||||
|
let mut received_plaintext: Vec<u8> = vec![0u8; received_ciphertext.len()];
|
||||||
|
let mut total_size = 0;
|
||||||
|
for chunk in received_ciphertext.chunks(NOISE_TRANSPORT_PER_PACKET_MAX) {
|
||||||
|
total_size += self
|
||||||
|
.transport
|
||||||
|
.read_message(chunk, &mut received_plaintext[total_size..])?;
|
||||||
|
}
|
||||||
|
received_plaintext.truncate(total_size);
|
||||||
|
Ok(received_plaintext)
|
||||||
|
}
|
||||||
|
}
|
199
rust/hsm-enclave/src/snow_resolver.rs
Normal file
199
rust/hsm-enclave/src/snow_resolver.rs
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 Signal Messenger, LLC.
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
//
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
|
use aes_gcm::aead::generic_array::typenum::Unsigned;
|
||||||
|
use aes_gcm::{AeadCore, AeadInPlace, NewAead};
|
||||||
|
use rand_core::{CryptoRng, RngCore};
|
||||||
|
use sha2::{Digest, Sha256};
|
||||||
|
use snow::params::{CipherChoice, DHChoice, HashChoice};
|
||||||
|
use snow::resolvers::CryptoResolver;
|
||||||
|
use snow::types::{Cipher, Dh, Hash, Random};
|
||||||
|
use x25519_dalek as x25519;
|
||||||
|
|
||||||
|
struct Rng<T>(T);
|
||||||
|
impl<T: RngCore> RngCore for Rng<T> {
|
||||||
|
fn next_u32(&mut self) -> u32 {
|
||||||
|
self.0.next_u32()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_u64(&mut self) -> u64 {
|
||||||
|
self.0.next_u64()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||||
|
self.0.fill_bytes(dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand_core::Error> {
|
||||||
|
self.0.try_fill_bytes(dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: CryptoRng> CryptoRng for Rng<T> {}
|
||||||
|
impl<T: RngCore + CryptoRng + Send + Sync> Random for Rng<T> {}
|
||||||
|
|
||||||
|
// From snow's resolvers/default.rs
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Dh25519 {
|
||||||
|
privkey: [u8; 32],
|
||||||
|
pubkey: [u8; 32],
|
||||||
|
}
|
||||||
|
impl Dh for Dh25519 {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"25519"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pub_len(&self) -> usize {
|
||||||
|
32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn priv_len(&self) -> usize {
|
||||||
|
32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self, privkey: &[u8]) {
|
||||||
|
self.privkey.copy_from_slice(privkey);
|
||||||
|
self.pubkey = x25519::x25519(self.privkey, x25519::X25519_BASEPOINT_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn generate(&mut self, rng: &mut dyn Random) {
|
||||||
|
rng.fill_bytes(&mut self.privkey);
|
||||||
|
self.pubkey = x25519::x25519(self.privkey, x25519::X25519_BASEPOINT_BYTES);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pubkey(&self) -> &[u8] {
|
||||||
|
&self.pubkey
|
||||||
|
}
|
||||||
|
|
||||||
|
fn privkey(&self) -> &[u8] {
|
||||||
|
&self.privkey
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dh(&self, pubkey: &[u8], out: &mut [u8]) -> Result<(), ()> {
|
||||||
|
let result = x25519::x25519(self.privkey, pubkey[..self.pub_len()].try_into().unwrap());
|
||||||
|
out[..result.len()].copy_from_slice(&result);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on snow's resolvers/default.rs
|
||||||
|
#[derive(Default)]
|
||||||
|
struct HashSHA256 {
|
||||||
|
hasher: Sha256,
|
||||||
|
}
|
||||||
|
impl Hash for HashSHA256 {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"sha256"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn block_len(&self) -> usize {
|
||||||
|
64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hash_len(&self) -> usize {
|
||||||
|
32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
self.hasher = Sha256::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn input(&mut self, data: &[u8]) {
|
||||||
|
self.hasher.update(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn result(&mut self, out: &mut [u8]) {
|
||||||
|
let hash = self.hasher.finalize_reset();
|
||||||
|
out[..hash.len()].copy_from_slice(&hash);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on snow's resolvers/default.rs
|
||||||
|
#[derive(Default)]
|
||||||
|
struct CipherAesGcm {
|
||||||
|
key: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cipher for CipherAesGcm {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"AESGCM"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set(&mut self, key: &[u8]) {
|
||||||
|
self.key.copy_from_slice(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn encrypt(&self, nonce: u64, authtext: &[u8], plaintext: &[u8], out: &mut [u8]) -> usize {
|
||||||
|
let aead = aes_gcm::Aes256Gcm::new(&self.key.into());
|
||||||
|
|
||||||
|
let mut nonce_bytes = [0u8; 12];
|
||||||
|
nonce_bytes[4..].copy_from_slice(&nonce.to_be_bytes());
|
||||||
|
|
||||||
|
let (ciphertext, rest_of_out) = out.split_at_mut(plaintext.len());
|
||||||
|
ciphertext.copy_from_slice(plaintext);
|
||||||
|
|
||||||
|
let tag = aead
|
||||||
|
.encrypt_in_place_detached(&nonce_bytes.into(), authtext, ciphertext)
|
||||||
|
.expect("Encryption failed!");
|
||||||
|
|
||||||
|
rest_of_out[..tag.len()].copy_from_slice(&tag);
|
||||||
|
|
||||||
|
plaintext.len() + <aes_gcm::Aes256Gcm as AeadCore>::TagSize::USIZE
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt(
|
||||||
|
&self,
|
||||||
|
nonce: u64,
|
||||||
|
authtext: &[u8],
|
||||||
|
ciphertext: &[u8],
|
||||||
|
out: &mut [u8],
|
||||||
|
) -> Result<usize, ()> {
|
||||||
|
let aead = aes_gcm::Aes256Gcm::new(&self.key.into());
|
||||||
|
|
||||||
|
let mut nonce_bytes = [0u8; 12];
|
||||||
|
nonce_bytes[4..].copy_from_slice(&nonce.to_be_bytes());
|
||||||
|
|
||||||
|
let (ciphertext, tag) = ciphertext
|
||||||
|
.split_at(ciphertext.len() - <aes_gcm::Aes256Gcm as AeadCore>::TagSize::USIZE);
|
||||||
|
|
||||||
|
let plaintext = &mut out[..ciphertext.len()];
|
||||||
|
plaintext.copy_from_slice(ciphertext);
|
||||||
|
|
||||||
|
aead.decrypt_in_place_detached(&nonce_bytes.into(), authtext, plaintext, tag.into())
|
||||||
|
.map(|_| plaintext.len())
|
||||||
|
.map_err(|_| ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Resolver;
|
||||||
|
|
||||||
|
impl CryptoResolver for Resolver {
|
||||||
|
fn resolve_rng(&self) -> Option<Box<dyn Random>> {
|
||||||
|
Some(Box::new(Rng(rand_core::OsRng)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_dh(&self, choice: &DHChoice) -> Option<Box<dyn Dh>> {
|
||||||
|
match choice {
|
||||||
|
DHChoice::Curve25519 => Some(Box::new(Dh25519::default())),
|
||||||
|
_ => panic!("{:?} not supported", choice),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_hash(&self, choice: &HashChoice) -> Option<Box<dyn Hash>> {
|
||||||
|
match choice {
|
||||||
|
HashChoice::SHA256 => Some(Box::new(HashSHA256::default())),
|
||||||
|
_ => panic!("{:?} not supported", choice),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_cipher(&self, choice: &CipherChoice) -> Option<Box<dyn Cipher>> {
|
||||||
|
match choice {
|
||||||
|
CipherChoice::AESGCM => Some(Box::new(CipherAesGcm::default())),
|
||||||
|
_ => panic!("{:?} not supported", choice),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
85
rust/hsm-enclave/tests/tests.rs
Normal file
85
rust/hsm-enclave/tests/tests.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2021 Signal Messenger, LLC.
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
//
|
||||||
|
|
||||||
|
use hsm_enclave::*;
|
||||||
|
const NOISE_PATTERN: &str = "Noise_NK_25519_AESGCM_SHA256";
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hsm_enclave_happy_path() -> Result<(), Error> {
|
||||||
|
// Spin up a handshake for the server-side.
|
||||||
|
let keypair = snow::Builder::new(NOISE_PATTERN.parse()?).generate_keypair()?;
|
||||||
|
let mut server_hs = snow::Builder::new(NOISE_PATTERN.parse()?)
|
||||||
|
.local_private_key(&keypair.private)
|
||||||
|
.build_responder()?;
|
||||||
|
|
||||||
|
// Spin up the client connection establishment.
|
||||||
|
let mut public_key = [0u8; 32];
|
||||||
|
public_key.copy_from_slice(&keypair.public);
|
||||||
|
let establishment = ClientConnectionEstablishment::new(public_key, vec![[1u8; 32]])?;
|
||||||
|
|
||||||
|
// Give the establishment message to the server.
|
||||||
|
let mut payload = vec![0u8; 32];
|
||||||
|
let read_size = server_hs.read_message(establishment.initial_request(), &mut payload)?;
|
||||||
|
assert_eq!(read_size, 32);
|
||||||
|
assert_eq!(payload, [1u8; 32]);
|
||||||
|
|
||||||
|
// Send message back to client, finish handshake.
|
||||||
|
let mut message = vec![0u8; 80];
|
||||||
|
let write_size = server_hs.write_message(&payload, &mut message)?;
|
||||||
|
assert_eq!(write_size, 80);
|
||||||
|
assert!(server_hs.is_handshake_finished());
|
||||||
|
let mut server_transport = server_hs.into_transport_mode()?;
|
||||||
|
|
||||||
|
// This should complete our connection establishment, now.
|
||||||
|
let mut conn = establishment.complete(&message)?;
|
||||||
|
|
||||||
|
// Send message server to client.
|
||||||
|
let mut svr_cli_message = vec![0u8; 19]; // size=3 + overhead=16
|
||||||
|
let svr_cli_write_size = server_transport.write_message(&[7, 8, 9], &mut svr_cli_message)?;
|
||||||
|
assert_eq!(svr_cli_write_size, 19);
|
||||||
|
assert_eq!([7, 8, 9], conn.recv(&svr_cli_message)?.as_slice());
|
||||||
|
|
||||||
|
// Send message client to server.
|
||||||
|
let cli_svr_message = conn.send(&[0xa, 0xb, 0xc])?;
|
||||||
|
let mut cli_svr_payload = vec![0u8; 3];
|
||||||
|
let cli_svr_read_size =
|
||||||
|
server_transport.read_message(&cli_svr_message, &mut cli_svr_payload)?;
|
||||||
|
assert_eq!(cli_svr_read_size, 3);
|
||||||
|
assert_eq!([0xAu8, 0xBu8, 0xCu8], cli_svr_payload.as_slice());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hsm_enclave_codehash_mismatch() -> Result<(), Error> {
|
||||||
|
// Spin up a handshake for the server-side.
|
||||||
|
let keypair = snow::Builder::new(NOISE_PATTERN.parse()?).generate_keypair()?;
|
||||||
|
let mut server_hs = snow::Builder::new(NOISE_PATTERN.parse()?)
|
||||||
|
.local_private_key(&keypair.private)
|
||||||
|
.build_responder()?;
|
||||||
|
|
||||||
|
// Spin up the client connection establishment.
|
||||||
|
let mut public_key = [0u8; 32];
|
||||||
|
public_key.copy_from_slice(&keypair.public);
|
||||||
|
let establishment = ClientConnectionEstablishment::new(public_key, vec![[1u8; 32]])?;
|
||||||
|
|
||||||
|
// Give the establishment message to the server.
|
||||||
|
let mut payload = vec![0u8; 32];
|
||||||
|
let read_size = server_hs.read_message(establishment.initial_request(), &mut payload)?;
|
||||||
|
assert_eq!(read_size, 32);
|
||||||
|
assert_eq!(payload, [1u8; 32]);
|
||||||
|
|
||||||
|
// Send message back to client, finish handshake.
|
||||||
|
let mismatched_payload = vec![2u8; 32];
|
||||||
|
let mut message = vec![0u8; 80];
|
||||||
|
let write_size = server_hs.write_message(&mismatched_payload, &mut message)?;
|
||||||
|
assert_eq!(write_size, 80);
|
||||||
|
|
||||||
|
// This should complete our connection establishment, now.
|
||||||
|
let out = establishment.complete(message.as_slice());
|
||||||
|
assert!(out.is_err());
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user