WIP: upgrade ring to 0.9.x (#133)
* add PublicKey and pkcs8 generate * add hash, and Verifier, cleanup DNSKEY usage * cleanup key_format * server compiling, all tests passing * cleanup warnings in standard build * Cleanup unneeded functions * fix --no-default-features * update changelog * fix rustls build * update ring * temporary patch for updated tokio-rustls * prepare for 0.10.2 release
This commit is contained in:
parent
6a0782693e
commit
ea7d81804b
@ -13,30 +13,25 @@ matrix:
|
||||
# no features
|
||||
- rust: stable
|
||||
env: MODULES="client"
|
||||
RUN_KCOV=1
|
||||
CLIENT_OPTIONS="--no-default-features"
|
||||
|
||||
# default features...
|
||||
- rust: stable
|
||||
env: MODULES="client"
|
||||
RUN_KCOV=1
|
||||
|
||||
# just openssl
|
||||
- rust: stable
|
||||
env: MODULES="client"
|
||||
RUN_KCOV=1
|
||||
CLIENT_OPTIONS="--no-default-features --features=openssl"
|
||||
|
||||
# just tls
|
||||
- rust: stable
|
||||
env: MODULES="client"
|
||||
RUN_KCOV=1
|
||||
CLIENT_OPTIONS="--no-default-features --features=tls"
|
||||
|
||||
# just ring
|
||||
- rust: stable
|
||||
env: MODULES="client"
|
||||
RUN_KCOV=1
|
||||
CLIENT_OPTIONS="--no-default-features --features=ring"
|
||||
|
||||
# min rust version
|
||||
|
15
CHANGELOG.md
15
CHANGELOG.md
@ -2,6 +2,21 @@
|
||||
All notable changes to this project will be documented in this file.
|
||||
This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## 0.10.2
|
||||
|
||||
### Fixed
|
||||
- Fixed format of ED25519 keys (@briansmith) #129
|
||||
|
||||
### Changed
|
||||
- Revamped signer and keypair to better deal with public key (possible breaking change)
|
||||
- Upgraded *ring* to 0.9.x series, requires pkcs8 for key storage
|
||||
- Dropped support for dangerous private key byte access (possible breaking change)
|
||||
- Upgraded tokio-rustls and rustls dependencies to support *ring* updates
|
||||
|
||||
### Added
|
||||
- PublicKey and Verifier for verifying with zero copy from KEY and DNSKEY (possible breaking change)
|
||||
- Pkcs8 as a supported KeyFormat for storage (possible breaking change)
|
||||
|
||||
## 0.10.1
|
||||
### Added
|
||||
- Added `From<IpAddr>` for Name (reverse DNS) #105
|
||||
|
110
Cargo.lock
generated
110
Cargo.lock
generated
@ -10,7 +10,7 @@ dependencies = [
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"native-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rusqlite 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -82,14 +82,17 @@ name = "backtrace-sys"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64"
|
||||
version = "0.2.1"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
@ -131,7 +134,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.24.1"
|
||||
version = "2.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -139,9 +142,9 @@ dependencies = [
|
||||
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -229,7 +232,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "gcc"
|
||||
version = "0.3.45"
|
||||
version = "0.3.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -279,7 +282,7 @@ name = "libsqlite3-sys"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
@ -350,7 +353,7 @@ name = "native-tls"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"openssl 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"schannel 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"security-framework 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"security-framework-sys 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -411,22 +414,22 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.9.11"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl-sys 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.11"
|
||||
version = "0.9.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -507,14 +510,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "ring"
|
||||
version = "0.7.6"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"gcc 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"libc 0.2.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rayon 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -541,15 +544,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "rustls"
|
||||
version = "0.5.8"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"base64 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webpki 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"webpki 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -644,7 +647,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thread-id"
|
||||
version = "3.0.0"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@ -664,7 +667,7 @@ name = "thread_local"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -710,18 +713,18 @@ version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tokio-rustls"
|
||||
version = "0.1.7"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustls 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustls 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
@ -750,22 +753,22 @@ version = "0.10.1"
|
||||
dependencies = [
|
||||
"backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.24.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-openssl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -776,7 +779,7 @@ dependencies = [
|
||||
"data-encoding 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"env_logger 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"trust-dns 0.10.1",
|
||||
]
|
||||
@ -797,16 +800,16 @@ name = "trust-dns-rustls"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustls 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustls 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-rustls 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"tokio-rustls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"trust-dns 0.10.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-segmentation"
|
||||
version = "1.1.0"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -824,7 +827,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "untrusted"
|
||||
version = "0.3.2"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -848,7 +851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.7.0"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
@ -858,13 +861,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
|
||||
[[package]]
|
||||
name = "webpki"
|
||||
version = "0.10.2"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"ring 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"ring 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -894,14 +896,14 @@ dependencies = [
|
||||
"checksum atty 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d912da0db7fa85514874458ca3651fe2cddace8d0b0505571dbdcd41ab490159"
|
||||
"checksum backtrace 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "346d7644f0b5f9bc73082d3b2236b69a05fd35cce0cfa3724e184e6a5c9e2a2f"
|
||||
"checksum backtrace-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "d192fd129132fbc97497c1f2ec2c2c5174e376b95f535199ef4fe0a293d33842"
|
||||
"checksum base64 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2015e3793554aa5b6007e3a72959e84c1070039e74f13dde08fa64afe1ddd892"
|
||||
"checksum base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "30e93c03064e7590d0466209155251b90c22e37fab1daf2771582598b5827557"
|
||||
"checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d"
|
||||
"checksum bitflags 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1370e9fc2a6ae53aea8b7a5110edbd08836ed87c88736dfabccade1c2b44bff4"
|
||||
"checksum byteorder 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c40977b0ee6b9885c9013cd41d9feffdd22deb3bb4dc3a71d901cc7a77de18c8"
|
||||
"checksum bytes 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f9edb851115d67d1f18680f9326901768a91d37875b87015518357c6ce22b553"
|
||||
"checksum cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "de1e760d7b6535af4241fca8bd8adf68e2e7edacc6b29f5d399050c5e48cf88c"
|
||||
"checksum chrono 0.2.25 (registry+https://github.com/rust-lang/crates.io-index)" = "9213f7cd7c27e95c2b57c49f0e69b1ea65b27138da84a170133fd21b07659c00"
|
||||
"checksum clap 2.24.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b7541069be0b8aec41030802abe8b5cdef0490070afaa55418adea93b1e431e0"
|
||||
"checksum clap 2.24.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6b8f69e518f967224e628896b54e41ff6acfb4dcfefc5076325c36525dac900f"
|
||||
"checksum core-foundation 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "25bfd746d203017f7d5cbd31ee5d8e17f94b6521c7af77ece6c9e4b2d4b16c67"
|
||||
"checksum core-foundation-sys 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "065a5d7ffdcbc8fa145d6f0746f3555025b9097a9e9cda59f7467abae670c78d"
|
||||
"checksum crypt32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e34988f7e069e0b2f3bfc064295161e489b2d4e04a2e4248fb94360cdf00b4ec"
|
||||
@ -913,7 +915,7 @@ dependencies = [
|
||||
"checksum error-chain 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "faa976b4fd2e4c2b2f3f486874b19e61944d3de3de8b61c9fcf835d583871bcc"
|
||||
"checksum foreign-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3e4056b9bd47f8ac5ba12be771f77a0dae796d1bbaaf5fd0b9c2d38b69b8a29d"
|
||||
"checksum futures 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "55f0008e13fc853f79ea8fc86e931486860d4c4c156cdffb59fa5f7fa833660a"
|
||||
"checksum gcc 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "40899336fb50db0c78710f53e87afc54d8c7266fb76262fecc78ca1a7f09deae"
|
||||
"checksum gcc 0.3.46 (registry+https://github.com/rust-lang/crates.io-index)" = "181e3cebba1d663bd92eb90e2da787e10597e027eb00de8d742b260a7850948f"
|
||||
"checksum gdi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0912515a8ff24ba900422ecda800b52f4016a56251922d397c576bf92c690518"
|
||||
"checksum iovec 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "29d062ee61fccdf25be172e70f34c9f6efc597e1fb8f6526e8437b2046ab26be"
|
||||
"checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d"
|
||||
@ -935,8 +937,8 @@ dependencies = [
|
||||
"checksum num-iter 0.1.33 (registry+https://github.com/rust-lang/crates.io-index)" = "f7d1891bd7b936f12349b7d1403761c8a0b85a18b148e9da4429d5d102c1a41e"
|
||||
"checksum num-traits 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "e1cbfa3781f3fe73dc05321bed52a06d2d491eaa764c52335cf4399f046ece99"
|
||||
"checksum num_cpus 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca313f1862c7ec3e0dfe8ace9fa91b1d9cb5c84ace3d00f5ec4216238e93c167"
|
||||
"checksum openssl 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "241bcf67b1bb8d19da97360a925730bdf5b6176d434ab8ded55b4ca632346e3a"
|
||||
"checksum openssl-sys 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "e5e0fd64cb2fa018ed2e7b2c8d9649114fe5da957c9a67432957f01e5dcc82e9"
|
||||
"checksum openssl 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bb5d1663b73d10c6a3eda53e2e9d0346f822394e7b858d7257718f65f61dfbe2"
|
||||
"checksum openssl-sys 0.9.12 (registry+https://github.com/rust-lang/crates.io-index)" = "3a5886d87d3e2a0d890bf62dc8944f5e3769a405f7e1e9ef6e517e47fd7a0897"
|
||||
"checksum pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "3a8b4c6b8165cd1a1cd4b9b120978131389f64bdaf456435caa41e630edba903"
|
||||
"checksum rand 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "022e0636ec2519ddae48154b028864bdce4eaf7d35226ab8e65c611be97b189d"
|
||||
"checksum rayon 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8c83adcb08e5b922e804fe1918142b422602ef11f2fd670b0b52218cb5984a20"
|
||||
@ -946,11 +948,11 @@ dependencies = [
|
||||
"checksum regex 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4278c17d0f6d62dfef0ab00028feb45bd7d2102843f80763474eeb1be8a10c01"
|
||||
"checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957"
|
||||
"checksum regex-syntax 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9191b1f57603095f105d317e375d19b1c9c5c3185ea9633a99a6dcbed04457"
|
||||
"checksum ring 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "621adad0185f587ad058bbecededfa2413f5f2e999563bc96349dead9e00d25c"
|
||||
"checksum ring 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "873ec7c2b7c9bf58024eb8f1bbc40a6499cd23c1adc59532f4af9e355f1de0f3"
|
||||
"checksum rusqlite 0.9.5 (registry+https://github.com/rust-lang/crates.io-index)" = "90bff7b0113c476086ea8b3c5fc2df177fe86c0a945a0665ea704f36dc230286"
|
||||
"checksum rustc-demangle 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3058a43ada2c2d0b92b3ae38007a2d0fa5e9db971be260e0171408a4ff471c95"
|
||||
"checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda"
|
||||
"checksum rustls 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)" = "fcfb2a577b14dca9caf13e6c6aa86d61158aa677347fbc155e5a1dac0f3b718f"
|
||||
"checksum rustls 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4779e2e35a2704f3981f3981e2bc983aa94b3c009f544f005f2d935856c6f269"
|
||||
"checksum schannel 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c8b291854e37196c2b67249e09d6bdeff410b19e1acf05558168e9c4413b4e95"
|
||||
"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d"
|
||||
"checksum secur32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3f412dfa83308d893101dd59c10d6fda8283465976c28c287c5c855bf8d216bc"
|
||||
@ -962,26 +964,26 @@ dependencies = [
|
||||
"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6"
|
||||
"checksum term_size 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2b6b55df3198cc93372e85dd2ed817f0e38ce8cc0f22eb32391bfad9c4bf209"
|
||||
"checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03"
|
||||
"checksum thread-id 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4437c97558c70d129e40629a5b385b3fb1ffac301e63941335e4d354081ec14a"
|
||||
"checksum thread-id 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8df7875b676fddfadffd96deea3b1124e5ede707d4884248931077518cf1f773"
|
||||
"checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5"
|
||||
"checksum thread_local 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c85048c6260d17cf486ceae3282d9fb6b90be220bf5b28c400f5485ffc29f0c7"
|
||||
"checksum time 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "ffd7ccbf969a892bf83f1e441126968a07a3941c24ff522a26af9f9f4585d1a3"
|
||||
"checksum tokio-core 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "febd81b3e2ef615c6c8077347b33f3f3deec3d708ecd08194c9707b7a1eccfc9"
|
||||
"checksum tokio-io 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "48f55df1341bb92281f229a6030bc2abffde2c7a44c6d6b802b7687dd8be0775"
|
||||
"checksum tokio-openssl 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "114355e60ae8cdd9a779843fbb0f7130b96f86b7ee46cfd32cdc79cb9e6ade74"
|
||||
"checksum tokio-rustls 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3aef535401926e052a30c961f2be93805d4c176d30e8365d160de27b01906efd"
|
||||
"checksum tokio-rustls 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1561db9191a4c0e4167bb6f655e882ca91ca541738bc3f1ec36f488fedd06138"
|
||||
"checksum tokio-tls 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "666266622d9a4d1974a0beda33d505999515b0c60edc0c3fda09784e56609a97"
|
||||
"checksum toml 0.1.30 (registry+https://github.com/rust-lang/crates.io-index)" = "0590d72182e50e879c4da3b11c6488dae18fccb1ae0c7a3eda18e16795844796"
|
||||
"checksum unicode-segmentation 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "18127285758f0e2c6cf325bb3f3d138a12fee27de4f23e146cd6a179f26c2cf3"
|
||||
"checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946"
|
||||
"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f"
|
||||
"checksum unreachable 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1f2ae5ddb18e1c92664717616dd9549dde73f539f01bd7b77c2edb2446bdff91"
|
||||
"checksum untrusted 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "193df64312e3515fd983ded55ad5bcaa7647a035804828ed757e832ce6029ef3"
|
||||
"checksum untrusted 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6b65243989ef6aacd9c0d6bd2b822765c3361d8ed352185a6f3a41f3a718c673"
|
||||
"checksum user32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4ef4711d107b21b410a3a974b1204d9accc8b10dad75d8324b5d755de1617d47"
|
||||
"checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f"
|
||||
"checksum utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "662fab6525a98beff2921d7f61a39e7d59e0b425ebc7d0d9e66d316e55124122"
|
||||
"checksum vec_map 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f8cdc8b93bd0198ed872357fb2e667f7125646b1762f16d60b2c96350d361897"
|
||||
"checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c"
|
||||
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
|
||||
"checksum webpki 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4155c3a648038ca6b3568ab34e7da1866592ef40b2543fea412461364ef3cbfc"
|
||||
"checksum webpki 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)" = "dd7bf9a0f93259c4e827b8d0d31b729971150a1f14d5217dfe9ad0045b53d678"
|
||||
"checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a"
|
||||
"checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc"
|
||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||
|
@ -2,10 +2,11 @@
|
||||
members = ["client", "compatibility", "native-tls", "rustls", "server"]
|
||||
|
||||
[replace]
|
||||
#"native-tls:0.1.1" = { path = "../rust-native-tls" }
|
||||
#"openssl:0.9.8" = { git = "https://github.com/sfackler/rust-openssl.git", features = ["v102", "v110"] }
|
||||
#"openssl:0.9.8" = { path = "../rust-openssl/openssl", features = ["v102", "v110"] }
|
||||
#"openssl-sys:0.9.8" = { git = "https://github.com/sfackler/rust-openssl.git", features = ["v102", "v110"] }
|
||||
#"openssl-sys:0.9.8" = { path = "../rust-openssl/openssl-sys", features = ["v102", "v110"] }
|
||||
#"native-tls:0.1.1" = { path = "../rust-native-tls" }
|
||||
#"tokio-rustls:0.2.0" = { git = "https://github.com/bluejekyll/tokio-rustls.git" }
|
||||
#"security-framework:0.1.10" = { git = "https://github.com/sfackler/rust-security-framework.git" }
|
||||
#"security-framework-sys:0.1.10" = { git = "https://github.com/sfackler/rust-security-framework.git" }
|
||||
|
@ -69,13 +69,13 @@ lazy_static = "^0.2.1"
|
||||
log = "^0.3.5"
|
||||
openssl = { version = "^0.9.8", features = ["v102", "v110"], optional = true }
|
||||
rand = "^0.3"
|
||||
ring = { version = "^0.7", optional = true }
|
||||
ring = { version = "^0.9", optional = true }
|
||||
rustc-serialize = "^0.3.18"
|
||||
time = "^0.1"
|
||||
tokio-core = "^0.1"
|
||||
tokio-io = "^0.1"
|
||||
tokio-openssl = { version = "^0.1", optional = true }
|
||||
untrusted = "^0.3"
|
||||
untrusted = "^0.5"
|
||||
|
||||
[dev-dependencies]
|
||||
openssl = { version = "^0.9.8", features = ["v102", "v110"], optional = false }
|
||||
|
@ -12,15 +12,13 @@ use std::rc::Rc;
|
||||
|
||||
use futures::*;
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
use chrono::Duration;
|
||||
use client::ClientHandle;
|
||||
use error::*;
|
||||
use op::{Message, OpCode, Query};
|
||||
use rr::{domain, DNSClass, RData, Record, RecordType};
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
use rr::dnssec::Verifier;
|
||||
use rr::dnssec::{Algorithm, SupportedAlgorithms, TrustAnchor};
|
||||
#[cfg(feature = "openssl")]
|
||||
use rr::dnssec::{KeyPair, Signer};
|
||||
use rr::rdata::{DNSKEY, SIG};
|
||||
use rr::rdata::opt::EdnsOption;
|
||||
|
||||
@ -684,7 +682,7 @@ fn verify_default_rrset<H>(client: SecureClientHandle<H>,
|
||||
}
|
||||
|
||||
/// Verifies the given SIG of the RRSET with the DNSKEY.
|
||||
#[cfg(feature = "openssl")]
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
fn verify_rrset_with_dnskey(dnskey: &DNSKEY, sig: &SIG, rrset: &Rrset) -> ClientResult<()> {
|
||||
if dnskey.revoke() {
|
||||
debug!("revoked");
|
||||
@ -693,42 +691,17 @@ fn verify_rrset_with_dnskey(dnskey: &DNSKEY, sig: &SIG, rrset: &Rrset) -> Client
|
||||
if !dnskey.zone_key() {
|
||||
return Err(ClientErrorKind::Message("is not a zone key").into());
|
||||
}
|
||||
if *dnskey.algorithm() != sig.algorithm() {
|
||||
if dnskey.algorithm() != sig.algorithm() {
|
||||
return Err(ClientErrorKind::Message("mismatched algorithm").into());
|
||||
}
|
||||
|
||||
let pkey = KeyPair::from_public_bytes(dnskey.public_key(), *dnskey.algorithm());
|
||||
if let Err(e) = pkey {
|
||||
debug!("error getting key from vec: {}", e);
|
||||
return Err(ClientErrorKind::Message("error getting key from vec").into());
|
||||
}
|
||||
let pkey = pkey.unwrap();
|
||||
|
||||
let signer: Signer = Signer::dnssec(dnskey.clone(),
|
||||
pkey,
|
||||
sig.signer_name().clone(),
|
||||
Duration::zero());
|
||||
|
||||
signer
|
||||
.hash_rrset_with_sig(&rrset.name, rrset.record_class, sig, &rrset.records)
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|rrset_hash| {
|
||||
signer
|
||||
.verify(&rrset_hash, sig.sig())
|
||||
.map(|_| {
|
||||
debug!("verified rrset: {}, type: {:?}",
|
||||
rrset.name,
|
||||
rrset.record_type);
|
||||
()
|
||||
})
|
||||
.map_err(|e| e.into())
|
||||
})
|
||||
dnskey.verify_rrsig(&rrset.name, rrset.record_class, sig, &rrset.records).map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Will always return an error. To enable record verification compile with the openssl feature.
|
||||
#[cfg(not(feature = "openssl"))]
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
fn verify_rrset_with_dnskey(_: &DNSKEY, _: &SIG, _: &Rrset) -> ClientResult<()> {
|
||||
Err(ClientErrorKind::Message("openssl feature not enabled").into())
|
||||
Err(ClientErrorKind::Message("openssl or ring feature(s) not enabled").into())
|
||||
}
|
||||
|
||||
|
||||
|
@ -127,7 +127,7 @@ impl Algorithm {
|
||||
10 => Ok(Algorithm::RSASHA512),
|
||||
13 => Ok(Algorithm::ECDSAP256SHA256),
|
||||
14 => Ok(Algorithm::ECDSAP384SHA384),
|
||||
15 => Ok(Algorithm::ED25519), // FIXME: assuming IANA will give this as the next number...
|
||||
15 => Ok(Algorithm::ED25519),
|
||||
_ => Err(DecodeErrorKind::UnknownAlgorithmTypeValue(value).into()),
|
||||
}
|
||||
}
|
||||
@ -203,7 +203,7 @@ impl From<Algorithm> for u8 {
|
||||
Algorithm::RSASHA512 => 10,
|
||||
Algorithm::ECDSAP256SHA256 => 13,
|
||||
Algorithm::ECDSAP384SHA384 => 14,
|
||||
Algorithm::ED25519 => 15, // FIXME: assuming IANA will give this as the next number...
|
||||
Algorithm::ED25519 => 15,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
364
client/src/rr/dnssec/hash.rs
Normal file
364
client/src/rr/dnssec/hash.rs
Normal file
@ -0,0 +1,364 @@
|
||||
//! hash functions for DNSSec operations
|
||||
|
||||
use op::Message;
|
||||
use rr::{DNSClass, Name, Record, RecordType, RData};
|
||||
use rr::dnssec::{Algorithm, DnsSecErrorKind, DnsSecResult};
|
||||
use rr::rdata::{sig, SIG};
|
||||
use serialize::binary::{BinEncoder, BinSerializable, EncodeMode};
|
||||
|
||||
/// Hashes a Message for signing
|
||||
pub fn hash_message(message: &Message, pre_sig0: &SIG) -> DnsSecResult<Vec<u8>> {
|
||||
// TODO: should perform the serialization and sign block by block to reduce the max memory
|
||||
// usage, though at 4k max, this is probably unnecessary... For AXFR and large zones, it's
|
||||
// more important
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(512);
|
||||
let mut buf2: Vec<u8> = Vec::with_capacity(512);
|
||||
|
||||
{
|
||||
let mut encoder: BinEncoder = BinEncoder::with_mode(&mut buf, EncodeMode::Normal);
|
||||
assert!(sig::emit_pre_sig(&mut encoder,
|
||||
pre_sig0.type_covered(),
|
||||
pre_sig0.algorithm(),
|
||||
pre_sig0.num_labels(),
|
||||
pre_sig0.original_ttl(),
|
||||
pre_sig0.sig_expiration(),
|
||||
pre_sig0.sig_inception(),
|
||||
pre_sig0.key_tag(),
|
||||
pre_sig0.signer_name())
|
||||
.is_ok());
|
||||
// need a separate encoder here, as the encoding references absolute positions
|
||||
// inside the buffer. If the buffer already contains the sig0 RDATA, offsets
|
||||
// are wrong and the signature won't match.
|
||||
let mut encoder2: BinEncoder = BinEncoder::with_mode(&mut buf2, EncodeMode::Signing);
|
||||
message.emit(&mut encoder2).unwrap(); // coding error if this panics (i think?)
|
||||
}
|
||||
|
||||
buf.append(&mut buf2);
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Computes the hash of the given record set
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - RRset record name
|
||||
/// * `dns_class` - DNSClass, i.e. IN, of the records
|
||||
/// * `num_labels` - number of labels in the name, needed to deal with `*.example.com`
|
||||
/// * `type_covered` - RecordType of the RRSet being hashed
|
||||
/// * `algorithm` - The Algorithm type used for the hashing
|
||||
/// * `original_ttl` - Original TTL is the TTL as specified in the SOA zones RRSet associated record
|
||||
/// * `sig_expiration` - the epoch seconds of when this hashed signature will expire
|
||||
/// * `key_inception` - the epoch seconds of when this hashed signature will be valid
|
||||
/// * `signer_name` - label of the etity responsible for signing this hash
|
||||
/// * `records` - RRSet to hash
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// the binary hash of the specified RRSet and associated information
|
||||
pub fn hash_rrset(name: &Name,
|
||||
dns_class: DNSClass,
|
||||
num_labels: u8,
|
||||
type_covered: RecordType,
|
||||
algorithm: Algorithm,
|
||||
original_ttl: u32,
|
||||
sig_expiration: u32,
|
||||
sig_inception: u32,
|
||||
key_tag: u16,
|
||||
signer_name: &Name,
|
||||
records: &[Record])
|
||||
-> DnsSecResult<Vec<u8>> {
|
||||
// TODO: change this to a BTreeSet so that it's preordered, no sort necessary
|
||||
let mut rrset: Vec<&Record> = Vec::new();
|
||||
|
||||
// collect only the records for this rrset
|
||||
for record in records {
|
||||
if dns_class == record.dns_class() && type_covered == record.rr_type() &&
|
||||
name == record.name() {
|
||||
rrset.push(record);
|
||||
}
|
||||
}
|
||||
|
||||
// put records in canonical order
|
||||
rrset.sort();
|
||||
|
||||
let name: Name = if let Some(name) = determine_name(name, num_labels) {
|
||||
name
|
||||
} else {
|
||||
return Err(DnsSecErrorKind::Msg(format!("could not determine name from {}", name)).into());
|
||||
};
|
||||
|
||||
// TODO: rather than buffering here, use the Signer/Verifier? might mean fewer allocations...
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
|
||||
{
|
||||
let mut encoder: BinEncoder = BinEncoder::new(&mut buf);
|
||||
encoder.set_canonical_names(true);
|
||||
|
||||
// signed_data = RRSIG_RDATA | RR(1) | RR(2)... where
|
||||
//
|
||||
// "|" denotes concatenation
|
||||
//
|
||||
// RRSIG_RDATA is the wire format of the RRSIG RDATA fields
|
||||
// with the Signature field excluded and the Signer's Name
|
||||
// in canonical form.
|
||||
assert!(sig::emit_pre_sig(&mut encoder,
|
||||
type_covered,
|
||||
algorithm,
|
||||
name.num_labels(),
|
||||
original_ttl,
|
||||
sig_expiration,
|
||||
sig_inception,
|
||||
key_tag,
|
||||
&signer_name)
|
||||
.is_ok());
|
||||
|
||||
// construct the rrset signing data
|
||||
for record in rrset {
|
||||
// RR(i) = name | type | class | OrigTTL | RDATA length | RDATA
|
||||
//
|
||||
// name is calculated according to the function in the RFC 4035
|
||||
assert!(name.to_lowercase()
|
||||
.emit_as_canonical(&mut encoder, true)
|
||||
.is_ok());
|
||||
//
|
||||
// type is the RRset type and all RRs in the class
|
||||
assert!(type_covered.emit(&mut encoder).is_ok());
|
||||
//
|
||||
// class is the RRset's class
|
||||
assert!(dns_class.emit(&mut encoder).is_ok());
|
||||
//
|
||||
// OrigTTL is the value from the RRSIG Original TTL field
|
||||
assert!(encoder.emit_u32(original_ttl).is_ok());
|
||||
//
|
||||
// RDATA length
|
||||
// TODO: add support to the encoder to set a marker to go back and write the length
|
||||
let mut rdata_buf = Vec::new();
|
||||
{
|
||||
let mut rdata_encoder = BinEncoder::new(&mut rdata_buf);
|
||||
rdata_encoder.set_canonical_names(true);
|
||||
assert!(record.rdata().emit(&mut rdata_encoder).is_ok());
|
||||
}
|
||||
assert!(encoder.emit_u16(rdata_buf.len() as u16).is_ok());
|
||||
//
|
||||
// All names in the RDATA field are in canonical form (set above)
|
||||
assert!(encoder.emit_vec(&rdata_buf).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This used to return the hash, now it's a hashable record type?
|
||||
// DigestType::from(self.algorithm).hash(&buf)
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// hashes the RRSet with information provided from the RRSig record
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `rrsig` - SIG or RRSIG record, which was produced from the RRSet
|
||||
/// * `records` - RRSet records to sign with the information in the `rrsig`
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// binary hash of the RRSet with the information from the RRSIG record
|
||||
pub fn hash_rrset_with_rrsig(rrsig: &Record, records: &[Record]) -> DnsSecResult<Vec<u8>> {
|
||||
if let &RData::SIG(ref sig) = rrsig.rdata() {
|
||||
hash_rrset_with_sig(rrsig.name(), rrsig.dns_class(), sig, records)
|
||||
} else {
|
||||
return Err(DnsSecErrorKind::Msg(format!("could not determine name from {}", rrsig.name()))
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
/// hashes the RRSet with information provided from the RRSig record
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - labels of the record to sign
|
||||
/// * `dns_class` - DNSClass of the RRSet, i.e. IN
|
||||
/// * `sig` - SIG or RRSIG record, which was produced from the RRSet
|
||||
/// * `records` - RRSet records to sign with the information in the `rrsig`
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// binary hash of the RRSet with the information from the RRSIG record
|
||||
pub fn hash_rrset_with_sig(name: &Name,
|
||||
dns_class: DNSClass,
|
||||
sig: &SIG,
|
||||
records: &[Record])
|
||||
-> DnsSecResult<Vec<u8>> {
|
||||
hash_rrset(name,
|
||||
dns_class,
|
||||
sig.num_labels(),
|
||||
sig.type_covered(),
|
||||
sig.algorithm(),
|
||||
sig.original_ttl(),
|
||||
sig.sig_expiration(),
|
||||
sig.sig_inception(),
|
||||
sig.key_tag(),
|
||||
sig.signer_name(),
|
||||
records)
|
||||
}
|
||||
|
||||
|
||||
/// [RFC 4035](https://tools.ietf.org/html/rfc4035), DNSSEC Protocol Modifications, March 2005
|
||||
///
|
||||
/// ```text
|
||||
///
|
||||
/// 5.3.2. Reconstructing the Signed Data
|
||||
/// ...
|
||||
/// To calculate the name:
|
||||
/// let rrsig_labels = the value of the RRSIG Labels field
|
||||
///
|
||||
/// let fqdn = RRset's fully qualified domain name in
|
||||
/// canonical form
|
||||
///
|
||||
/// let fqdn_labels = Label count of the fqdn above.
|
||||
///
|
||||
/// if rrsig_labels = fqdn_labels,
|
||||
/// name = fqdn
|
||||
///
|
||||
/// if rrsig_labels < fqdn_labels,
|
||||
/// name = "*." | the rightmost rrsig_label labels of the
|
||||
/// fqdn
|
||||
///
|
||||
/// if rrsig_labels > fqdn_labels
|
||||
/// the RRSIG RR did not pass the necessary validation
|
||||
/// checks and MUST NOT be used to authenticate this
|
||||
/// RRset.
|
||||
///
|
||||
/// The canonical forms for names and RRsets are defined in [RFC4034].
|
||||
/// ```
|
||||
pub fn determine_name(name: &Name, num_labels: u8) -> Option<Name> {
|
||||
// To calculate the name:
|
||||
// let rrsig_labels = the value of the RRSIG Labels field
|
||||
//
|
||||
// let fqdn = RRset's fully qualified domain name in
|
||||
// canonical form
|
||||
//
|
||||
// let fqdn_labels = Label count of the fqdn above.
|
||||
let fqdn_labels = name.num_labels();
|
||||
// if rrsig_labels = fqdn_labels,
|
||||
// name = fqdn
|
||||
|
||||
if fqdn_labels == num_labels {
|
||||
return Some(name.clone());
|
||||
}
|
||||
// if rrsig_labels < fqdn_labels,
|
||||
// name = "*." | the rightmost rrsig_label labels of the
|
||||
// fqdn
|
||||
if num_labels < fqdn_labels {
|
||||
let mut star_name: Name = Name::new().label("*");
|
||||
let rightmost = name.trim_to(num_labels as usize);
|
||||
if !rightmost.is_root() {
|
||||
star_name.append(&rightmost);
|
||||
return Some(star_name);
|
||||
}
|
||||
return Some(star_name);
|
||||
}
|
||||
//
|
||||
// if rrsig_labels > fqdn_labels
|
||||
// the RRSIG RR did not pass the necessary validation
|
||||
// checks and MUST NOT be used to authenticate this
|
||||
// RRset.
|
||||
// TODO: this should be an error
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
extern crate openssl;
|
||||
use self::openssl::rsa::Rsa;
|
||||
|
||||
use rr::{Name, RecordType};
|
||||
use rr::rdata::SIG;
|
||||
use rr::dnssec::{KeyPair, Signer};
|
||||
|
||||
pub use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_hash_rrset() {
|
||||
let rsa = Rsa::generate(512).unwrap();
|
||||
let key = KeyPair::from_rsa(rsa).unwrap();
|
||||
let sig0key = key.to_sig0key(Algorithm::RSASHA256).unwrap();
|
||||
let signer = Signer::sig0(sig0key, key, Name::root());
|
||||
|
||||
let origin: Name = Name::parse("example.com.", None).unwrap();
|
||||
let rrsig = Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::SIG(SIG::new(RecordType::NS,
|
||||
Algorithm::RSASHA256,
|
||||
origin.num_labels(),
|
||||
86400,
|
||||
5,
|
||||
0,
|
||||
signer.calculate_key_tag().unwrap(),
|
||||
origin.clone(),
|
||||
vec![])))
|
||||
.clone();
|
||||
let rrset =
|
||||
vec![Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()))
|
||||
.clone(),
|
||||
Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()))
|
||||
.clone()];
|
||||
|
||||
let hash = hash_rrset_with_rrsig(&rrsig, &rrset).unwrap();
|
||||
assert!(!hash.is_empty());
|
||||
|
||||
let rrset =
|
||||
vec![Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::CNAME)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::CNAME(Name::parse("a.iana-servers.net.", None)
|
||||
.unwrap()))
|
||||
.clone(), // different type
|
||||
Record::new()
|
||||
.set_name(Name::parse("www.example.com.", None).unwrap())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()))
|
||||
.clone(), // different name
|
||||
Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::CH)
|
||||
.set_rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()))
|
||||
.clone(), // different class
|
||||
Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()))
|
||||
.clone(),
|
||||
Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()))
|
||||
.clone()];
|
||||
|
||||
let filtered_hash = hash_rrset_with_rrsig(&rrsig, &rrset).unwrap();
|
||||
assert!(!filtered_hash.is_empty());
|
||||
assert_eq!(hash, filtered_hash);
|
||||
}
|
||||
}
|
@ -4,6 +4,10 @@ use openssl::ec::EcKey;
|
||||
use openssl::rsa::Rsa;
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::symm::Cipher;
|
||||
#[cfg(feature = "ring")]
|
||||
use ring::signature::Ed25519KeyPair;
|
||||
#[cfg(feature = "ring")]
|
||||
use untrusted::Input;
|
||||
|
||||
use error::*;
|
||||
use rr::dnssec::Algorithm;
|
||||
@ -16,8 +20,8 @@ pub enum KeyFormat {
|
||||
Der,
|
||||
/// A pem encoded key, the default of OpenSSL
|
||||
Pem,
|
||||
/// Raw bytes unformatted
|
||||
Raw,
|
||||
/// Pkcs8, a pkcs8 formatted private key
|
||||
Pkcs8,
|
||||
}
|
||||
|
||||
impl KeyFormat {
|
||||
@ -89,27 +93,107 @@ impl KeyFormat {
|
||||
}
|
||||
Algorithm::ED25519 => {
|
||||
match self {
|
||||
KeyFormat::Raw => {
|
||||
return KeyPair::from_private_bytes(algorithm, bytes)
|
||||
.map_err(|e| format!("error reading ED25519 as RAW: {}", e).into())
|
||||
#[cfg(feature = "ring")]
|
||||
KeyFormat::Pkcs8 => {
|
||||
let key = try!(Ed25519KeyPair::from_pkcs8(Input::from(bytes)));
|
||||
|
||||
return Ok(KeyPair::from_ed25519(key));
|
||||
}
|
||||
e @ _ => {
|
||||
return Err(format!("unsupported key format with ED25519 (RAW only): {:?}",
|
||||
return Err(format!("unsupported key format with ED25519 (only Pkcs8 supported): {:?}",
|
||||
e)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "openssl"))]
|
||||
#[cfg(not(all(feature = "openssl", feature = "ring")))]
|
||||
e @ _ => {
|
||||
return Err(format!("unsupported Algorith, enable openssl feature: {:?}", e).into())
|
||||
return Err(format!("unsupported Algorithm, enable openssl or ring feature: {:?}",
|
||||
e)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a new key and encode to the specified format
|
||||
pub fn generate_and_encode(self,
|
||||
algorithm: Algorithm,
|
||||
password: Option<&str>)
|
||||
-> DnsSecResult<Vec<u8>> {
|
||||
// on encoding, if the password is empty string, ignore it (empty string is ok on decode)
|
||||
#[allow(unused)]
|
||||
let password = password
|
||||
.iter()
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.as_bytes())
|
||||
.next();
|
||||
|
||||
// generate the key
|
||||
#[allow(unused)]
|
||||
let key_pair: KeyPair = match algorithm {
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::RSASHA1 |
|
||||
Algorithm::RSASHA1NSEC3SHA1 |
|
||||
Algorithm::RSASHA256 |
|
||||
Algorithm::RSASHA512 |
|
||||
Algorithm::ECDSAP256SHA256 |
|
||||
Algorithm::ECDSAP384SHA384 => KeyPair::generate(algorithm)?,
|
||||
#[cfg(feature = "ring")]
|
||||
Algorithm::ED25519 => return KeyPair::generate_pkcs8(algorithm),
|
||||
#[cfg(not(all(feature = "openssl", feature = "ring")))]
|
||||
e @ _ => {
|
||||
return Err(format!("unsupported Algorithm, enable openssl or ring feature: {:?}",
|
||||
e)
|
||||
.into())
|
||||
}
|
||||
};
|
||||
|
||||
// encode the key
|
||||
#[allow(unreachable_code)] // in ring only mode, this block is never reached
|
||||
match key_pair {
|
||||
#[cfg(feature = "openssl")]
|
||||
KeyPair::EC(ref pkey) |
|
||||
KeyPair::RSA(ref pkey) => {
|
||||
match self {
|
||||
KeyFormat::Der => {
|
||||
// to avoid accientally storing a key where there was an expectation that it was password protected
|
||||
if password.is_some() {
|
||||
return Err(format!("Can only password protect PEM: {:?}", self).into());
|
||||
}
|
||||
return pkey.private_key_to_der()
|
||||
.map_err(|e| {
|
||||
format!("error writing key as DER: {}", e).into()
|
||||
});
|
||||
}
|
||||
KeyFormat::Pem => {
|
||||
let key = if let Some(password) = password {
|
||||
pkey.private_key_to_pem_passphrase(Cipher::aes_256_cbc(), password)
|
||||
} else {
|
||||
pkey.private_key_to_pem()
|
||||
};
|
||||
|
||||
return key.map_err(|e| format!("error writing key as PEM: {}", e).into());
|
||||
}
|
||||
e @ _ => {
|
||||
return Err(format!("unsupported key format with RSA or EC (DER or PEM \
|
||||
only): {:?}",
|
||||
e)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
KeyPair::ED25519(..) => panic!("should have returned early"),
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
_ => return Err(format!("unsupported Algorithm, enable openssl feature (encode not supported with ring)").into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Decode private key
|
||||
#[deprecated]
|
||||
pub fn encode_key(self, key_pair: &KeyPair, password: Option<&str>) -> DnsSecResult<Vec<u8>> {
|
||||
// on encoding, if the password is empty string, ignore it (empty string is ok on decode)
|
||||
#[allow(unused)]
|
||||
let password = password
|
||||
.iter()
|
||||
.filter(|s| !s.is_empty())
|
||||
@ -148,27 +232,8 @@ impl KeyFormat {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "ring")] KeyPair::ED25519(..) => {
|
||||
match self {
|
||||
KeyFormat::Raw => {
|
||||
// to avoid accientally storing a key where there was an expectation that it was password protected
|
||||
if password.is_some() {
|
||||
return Err(format!("Can only password protect PEM: {:?}", self).into());
|
||||
}
|
||||
return key_pair
|
||||
.to_private_bytes()
|
||||
.map_err(|e| {
|
||||
format!("error writing ED25519 as RAW: {}", e)
|
||||
.into()
|
||||
});
|
||||
}
|
||||
e @ _ => {
|
||||
return Err(format!("unsupported key format with ED25519 (RAW only): {:?}",
|
||||
e)
|
||||
.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(any(feature = "ring", not(feature = "openssl")))]
|
||||
_ => return Err(format!("unsupported Algorithm, enable openssl feature (encode not supported with ring)").into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -191,14 +256,6 @@ mod tests {
|
||||
encode_decode_with_format(KeyFormat::Pem, algorithm, true, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "openssl")]
|
||||
fn test_rsa_encode_decode_raw() {
|
||||
let algorithm = Algorithm::RSASHA256;
|
||||
encode_decode_with_format(KeyFormat::Raw, algorithm, false, false);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "openssl")]
|
||||
fn test_ec_encode_decode_der() {
|
||||
@ -213,21 +270,11 @@ mod tests {
|
||||
encode_decode_with_format(KeyFormat::Pem, algorithm, true, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "openssl")]
|
||||
fn test_ec_encode_decode_raw() {
|
||||
let algorithm = Algorithm::ECDSAP256SHA256;
|
||||
encode_decode_with_format(KeyFormat::Raw, algorithm, false, false);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "ring")]
|
||||
fn test_ed25519_encode_decode() {
|
||||
fn test_ed25519_encode_decode_pkcs8() {
|
||||
let algorithm = Algorithm::ED25519;
|
||||
encode_decode_with_format(KeyFormat::Der, algorithm, false, false);
|
||||
encode_decode_with_format(KeyFormat::Pem, algorithm, false, false);
|
||||
encode_decode_with_format(KeyFormat::Raw, algorithm, false, true);
|
||||
encode_decode_with_format(KeyFormat::Pkcs8, algorithm, true, true);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -235,64 +282,60 @@ mod tests {
|
||||
algorithm: Algorithm,
|
||||
ok_pass: bool,
|
||||
ok_empty_pass: bool) {
|
||||
let keypair = KeyPair::generate(algorithm).unwrap();
|
||||
let password = Some("test password");
|
||||
let empty_password = Some("");
|
||||
let no_password = None::<&str>;
|
||||
|
||||
encode_decode_with_password(key_format, password, password, algorithm, ok_pass, true);
|
||||
encode_decode_with_password(key_format,
|
||||
&keypair,
|
||||
password,
|
||||
password,
|
||||
algorithm,
|
||||
ok_pass,
|
||||
true);
|
||||
encode_decode_with_password(key_format,
|
||||
&keypair,
|
||||
empty_password,
|
||||
empty_password,
|
||||
algorithm,
|
||||
ok_empty_pass,
|
||||
true);
|
||||
encode_decode_with_password(key_format,
|
||||
&keypair,
|
||||
no_password,
|
||||
no_password,
|
||||
algorithm,
|
||||
ok_empty_pass,
|
||||
true);
|
||||
encode_decode_with_password(key_format,
|
||||
&keypair,
|
||||
no_password,
|
||||
empty_password,
|
||||
algorithm,
|
||||
ok_empty_pass,
|
||||
true);
|
||||
encode_decode_with_password(key_format,
|
||||
&keypair,
|
||||
empty_password,
|
||||
no_password,
|
||||
algorithm,
|
||||
ok_empty_pass,
|
||||
true);
|
||||
encode_decode_with_password(key_format,
|
||||
&keypair,
|
||||
password,
|
||||
no_password,
|
||||
algorithm,
|
||||
ok_pass,
|
||||
false);
|
||||
// TODO: disabled for now... add back in if ring suports passwords on pkcs8
|
||||
// encode_decode_with_password(key_format,
|
||||
// password,
|
||||
// no_password,
|
||||
// algorithm,
|
||||
// ok_pass,
|
||||
// false);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn encode_decode_with_password(key_format: KeyFormat,
|
||||
keypair: &KeyPair,
|
||||
en_pass: Option<&str>,
|
||||
de_pass: Option<&str>,
|
||||
algorithm: Algorithm,
|
||||
encode: bool,
|
||||
decode: bool) {
|
||||
let encoded = key_format.encode_key(&keypair, en_pass);
|
||||
println!("test params: format: {:?}, en_pass: {:?}, de_pass: {:?}, alg: {:?}, encode: {}, decode: {}",
|
||||
key_format,
|
||||
en_pass,
|
||||
de_pass,
|
||||
algorithm,
|
||||
encode,
|
||||
decode);
|
||||
let encoded = key_format.generate_and_encode(algorithm, en_pass);
|
||||
|
||||
if encode {
|
||||
assert!(encoded.is_ok(), format!("{}", encoded.unwrap_err()));
|
||||
let decoded = key_format.decode_key(&encoded.unwrap(), de_pass, algorithm);
|
||||
|
@ -12,22 +12,22 @@ use openssl::sign::{Signer, Verifier};
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::pkey::PKey;
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::bn::{BigNum, BigNumContext};
|
||||
use openssl::bn::BigNumContext;
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::ec::{EcGroup, EcKey, EcPoint, POINT_CONVERSION_UNCOMPRESSED};
|
||||
use openssl::ec::{EcGroup, EcKey, POINT_CONVERSION_UNCOMPRESSED};
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::nid;
|
||||
|
||||
#[cfg(feature = "ring")]
|
||||
use ring::rand;
|
||||
#[cfg(feature = "ring")]
|
||||
use ring::signature::{Ed25519KeyPair, Ed25519KeyPairBytes, EdDSAParameters, VerificationAlgorithm};
|
||||
#[cfg(feature = "ring")]
|
||||
use untrusted::Input;
|
||||
use ring::signature::Ed25519KeyPair;
|
||||
|
||||
use error::*;
|
||||
use rr::Name;
|
||||
use rr::dnssec::{Algorithm, DigestType};
|
||||
use rr::dnssec::{Algorithm, DigestType, PublicKey};
|
||||
#[cfg(feature = "ring")]
|
||||
use rr::dnssec::public_key::Ed25519;
|
||||
use rr::rdata::{DNSKEY, DS, KEY};
|
||||
|
||||
/// A public and private key pair, the private portion is not required.
|
||||
@ -44,10 +44,9 @@ pub enum KeyPair {
|
||||
EC(PKey),
|
||||
/// ED25519 ecryption and hash defined keypair
|
||||
#[cfg(feature = "ring")]
|
||||
ED25519(Ed25519KeyPairBytes),
|
||||
ED25519(Ed25519KeyPair),
|
||||
}
|
||||
|
||||
|
||||
impl KeyPair {
|
||||
/// Creates an RSA type keypair.
|
||||
#[cfg(feature = "openssl")]
|
||||
@ -79,158 +78,10 @@ impl KeyPair {
|
||||
|
||||
/// Creates an ED25519 keypair.
|
||||
#[cfg(feature = "ring")]
|
||||
pub fn from_ed25519(ed_key: Ed25519KeyPairBytes) -> Self {
|
||||
pub fn from_ed25519(ed_key: Ed25519KeyPair) -> Self {
|
||||
KeyPair::ED25519(ed_key)
|
||||
}
|
||||
|
||||
/// Converts an array of bytes into a KeyPair, interpreting it based on the algorithm type.
|
||||
///
|
||||
/// Formats for the public key are described in rfc3110, rfc6605, and
|
||||
/// rfc-draft-ietf-curdle-dnskey-eddsa-03.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `public_key` - the public key bytes formatted in BigEndian/NetworkByteOrder
|
||||
/// * `algorithm` - the Algorithm which is used to interpret the key
|
||||
#[allow(unused)]
|
||||
pub fn from_public_bytes(public_key: &[u8], algorithm: Algorithm) -> DnsSecResult<Self> {
|
||||
match algorithm {
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::RSASHA1 |
|
||||
Algorithm::RSASHA1NSEC3SHA1 |
|
||||
Algorithm::RSASHA256 |
|
||||
Algorithm::RSASHA512 => {
|
||||
// RFC 3110 RSA SIGs and KEYs in the DNS May 2001
|
||||
//
|
||||
// 2. RSA Public KEY Resource Records
|
||||
//
|
||||
// RSA public keys are stored in the DNS as KEY RRs using algorithm
|
||||
// number 5 [RFC2535]. The structure of the algorithm specific portion
|
||||
// of the RDATA part of such RRs is as shown below.
|
||||
//
|
||||
// Field Size
|
||||
// ----- ----
|
||||
// exponent length 1 or 3 octets (see text)
|
||||
// exponent as specified by length field
|
||||
// modulus remaining space
|
||||
//
|
||||
// For interoperability, the exponent and modulus are each limited to
|
||||
// 4096 bits in length. The public key exponent is a variable length
|
||||
// unsigned integer. Its length in octets is represented as one octet
|
||||
// if it is in the range of 1 to 255 and by a zero octet followed by a
|
||||
// two octet unsigned length if it is longer than 255 bytes. The public
|
||||
// key modulus field is a multiprecision unsigned integer. The length
|
||||
// of the modulus can be determined from the RDLENGTH and the preceding
|
||||
// RDATA fields including the exponent. Leading zero octets are
|
||||
// prohibited in the exponent and modulus.
|
||||
//
|
||||
// Note: KEY RRs for use with RSA/SHA1 DNS signatures MUST use this
|
||||
// algorithm number (rather than the algorithm number specified in the
|
||||
// obsoleted RFC 2537).
|
||||
//
|
||||
// Note: This changes the algorithm number for RSA KEY RRs to be the
|
||||
// same as the new algorithm number for RSA/SHA1 SIGs.
|
||||
if public_key.len() < 3 || public_key.len() > (4096 + 3) {
|
||||
return Err(DnsSecErrorKind::Message("bad public key").into());
|
||||
}
|
||||
let mut num_exp_len_octs = 1;
|
||||
let mut len: u16 = public_key[0] as u16;
|
||||
if len == 0 {
|
||||
num_exp_len_octs = 3;
|
||||
len = ((public_key[1] as u16) << 8) | (public_key[2] as u16)
|
||||
}
|
||||
let len = len; // demut
|
||||
|
||||
// FYI: BigNum slices treat all slices as BigEndian, i.e NetworkByteOrder
|
||||
let e = try!(BigNum::from_slice(&public_key[(num_exp_len_octs as usize)..
|
||||
(len as usize + num_exp_len_octs)]));
|
||||
let n = try!(BigNum::from_slice(&public_key[(len as usize + num_exp_len_octs)..]));
|
||||
|
||||
OpenSslRsa::from_public_components(n, e)
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|rsa| Self::from_rsa(rsa))
|
||||
}
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::ECDSAP256SHA256 => {
|
||||
// RFC 6605 ECDSA for DNSSEC April 2012
|
||||
//
|
||||
// 4. DNSKEY and RRSIG Resource Records for ECDSA
|
||||
//
|
||||
// ECDSA public keys consist of a single value, called "Q" in FIPS
|
||||
// 186-3. In DNSSEC keys, Q is a simple bit string that represents the
|
||||
// uncompressed form of a curve point, "x | y".
|
||||
//
|
||||
// The ECDSA signature is the combination of two non-negative integers,
|
||||
// called "r" and "s" in FIPS 186-3. The two integers, each of which is
|
||||
// formatted as a simple octet string, are combined into a single longer
|
||||
// octet string for DNSSEC as the concatenation "r | s". (Conversion of
|
||||
// the integers to bit strings is described in Section C.2 of FIPS
|
||||
// 186-3.) For P-256, each integer MUST be encoded as 32 octets; for
|
||||
// P-384, each integer MUST be encoded as 48 octets.
|
||||
//
|
||||
// The algorithm numbers associated with the DNSKEY and RRSIG resource
|
||||
// records are fully defined in the IANA Considerations section. They
|
||||
// are:
|
||||
//
|
||||
// o DNSKEY and RRSIG RRs signifying ECDSA with the P-256 curve and
|
||||
// SHA-256 use the algorithm number 13.
|
||||
//
|
||||
// o DNSKEY and RRSIG RRs signifying ECDSA with the P-384 curve and
|
||||
// SHA-384 use the algorithm number 14.
|
||||
//
|
||||
// Conformant implementations that create records to be put into the DNS
|
||||
// MUST implement signing and verification for both of the above
|
||||
// algorithms. Conformant DNSSEC verifiers MUST implement verification
|
||||
// for both of the above algorithms.
|
||||
EcGroup::from_curve_name(nid::SECP256K1)
|
||||
.and_then(|group| BigNumContext::new().map(|ctx| (group, ctx)))
|
||||
// FYI: BigNum slices treat all slices as BigEndian, i.e NetworkByteOrder
|
||||
.and_then(|(group, mut ctx)| EcPoint::from_bytes(&group, public_key, &mut ctx).map(|point| (group, point) ))
|
||||
.and_then(|(group, point)| EcKey::from_public_key(&group, &point))
|
||||
.and_then(|ec_key| PKey::from_ec_key(ec_key) )
|
||||
.map(|pkey| KeyPair::EC(pkey))
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::ECDSAP384SHA384 => {
|
||||
// see above Algorithm::ECDSAP256SHA256 for reference
|
||||
EcGroup::from_curve_name(nid::SECP384R1)
|
||||
.and_then(|group| BigNumContext::new().map(|ctx| (group, ctx)))
|
||||
// FYI: BigNum slices treat all slices as BigEndian, i.e NetworkByteOrder
|
||||
.and_then(|(group, mut ctx)| EcPoint::from_bytes(&group, public_key, &mut ctx).map(|point| (group, point) ))
|
||||
.and_then(|(group, point)| EcKey::from_public_key(&group, &point))
|
||||
.and_then(|ec_key| PKey::from_ec_key(ec_key) )
|
||||
.map(|pkey| KeyPair::EC(pkey))
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
Algorithm::ED25519 => {
|
||||
// Internet-Draft EdDSA for DNSSEC December 2016
|
||||
//
|
||||
// An Ed25519 public key consists of a 32-octet value, which is encoded
|
||||
// into the Public Key field of a DNSKEY resource record as a simple bit
|
||||
// string. The generation of a public key is defined in Section 5.1.5
|
||||
// in [RFC 8032]. Breaking tradition, the keys are encoded in little-
|
||||
// endian byte order.
|
||||
if public_key.len() != 32 {
|
||||
return Err(DnsSecErrorKind::Msg(format!("expected 32 byte public_key: {}",
|
||||
public_key.len()))
|
||||
.into());
|
||||
}
|
||||
|
||||
let mut ed_key_pair = Ed25519KeyPairBytes {
|
||||
private_key: [0_u8; 32],
|
||||
public_key: [0_u8; 32],
|
||||
};
|
||||
|
||||
ed_key_pair.public_key.copy_from_slice(&public_key);
|
||||
Ok(KeyPair::ED25519(ed_key_pair))
|
||||
}
|
||||
#[cfg(not(all(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts this keypair to the DNS binary form of the public_key.
|
||||
///
|
||||
/// If there is a private key associated with this keypair, it will not be included in this
|
||||
@ -289,11 +140,9 @@ impl KeyPair {
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
KeyPair::ED25519(ref ed_key) => {
|
||||
Ok(ed_key.public_key.to_vec())
|
||||
}
|
||||
// #[cfg(not(all(feature = "openssl", feature = "ring")))]
|
||||
// _ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
KeyPair::ED25519(ref ed_key) => Ok(ed_key.public_key_bytes().to_vec()),
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl or ring feature(s) not enabled").into()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -445,133 +294,12 @@ impl KeyPair {
|
||||
signer.finish().map_err(|e| e.into())
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
KeyPair::ED25519(ref ed_key) => {
|
||||
Ed25519KeyPair::from_bytes(&ed_key.private_key, &ed_key.public_key)
|
||||
.map_err(|_| {
|
||||
DnsSecErrorKind::Message("something is wrong with the keys").into()
|
||||
})
|
||||
.map(|ed_key| ed_key.sign(message).as_slice().to_vec())
|
||||
}
|
||||
KeyPair::ED25519(ref ed_key) => Ok(ed_key.sign(message).as_ref().to_vec()),
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies the hash matches the signature with the current `key`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `message` - the message to be validated, see `hash_rrset`
|
||||
/// * `signature` - the signature to use to verify the hash, extracted from an `RData::RRSIG`
|
||||
/// for example.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// True if and only if the signature is valid for the hash. This will always return
|
||||
/// false if the `key`.
|
||||
#[allow(unused)]
|
||||
pub fn verify(&self,
|
||||
algorithm: Algorithm,
|
||||
message: &[u8],
|
||||
signature: &[u8])
|
||||
-> DnsSecResult<()> {
|
||||
match *self {
|
||||
#[cfg(feature = "openssl")]
|
||||
KeyPair::RSA(ref pkey) |
|
||||
KeyPair::EC(ref pkey) => {
|
||||
let digest_type = try!(DigestType::from(algorithm).to_openssl_digest());
|
||||
let mut verifier = Verifier::new(digest_type, &pkey).unwrap();
|
||||
try!(verifier.update(message));
|
||||
verifier
|
||||
.finish(signature)
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|b| if b {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(DnsSecErrorKind::Message("could not verify").into())
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
KeyPair::ED25519(ref ed_key) => {
|
||||
let public_key = Input::from(&ed_key.public_key);
|
||||
let message = Input::from(message);
|
||||
let signature = Input::from(signature);
|
||||
EdDSAParameters {}
|
||||
.verify(public_key, message, signature)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// The KeyPair, with private key, converted to binary form.
|
||||
///
|
||||
/// Generally the format is will be in PEM, with the exception of ED25519, which is
|
||||
/// currently little endian `32 private key bytes | 32 public key bytes`.
|
||||
pub fn to_private_bytes(&self) -> DnsSecResult<Vec<u8>> {
|
||||
match *self {
|
||||
#[cfg(feature = "openssl")]
|
||||
KeyPair::RSA(ref pkey) |
|
||||
KeyPair::EC(ref pkey) => pkey.private_key_to_pem().map_err(|e| e.into()),
|
||||
#[cfg(feature = "ring")]
|
||||
KeyPair::ED25519(ref ed_key) => {
|
||||
let mut vec = Vec::with_capacity(ed_key.private_key.len() +
|
||||
ed_key.public_key.len());
|
||||
|
||||
vec.extend_from_slice(&ed_key.private_key);
|
||||
vec.extend_from_slice(&ed_key.public_key);
|
||||
Ok(vec)
|
||||
}
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a KeyPair for the specified algorithm with the associated bytes
|
||||
///
|
||||
/// Generally the format is expected to be in PEM, with the exception of ED25519, which is
|
||||
/// currently little endian `32 private key bytes | 32 public key bytes`.
|
||||
#[allow(unused)]
|
||||
pub fn from_private_bytes(algorithm: Algorithm, bytes: &[u8]) -> DnsSecResult<Self> {
|
||||
match algorithm {
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::RSASHA1 |
|
||||
Algorithm::RSASHA1NSEC3SHA1 |
|
||||
Algorithm::RSASHA256 |
|
||||
Algorithm::RSASHA512 => {
|
||||
let rsa = try!(OpenSslRsa::private_key_from_pem(bytes));
|
||||
KeyPair::from_rsa(rsa)
|
||||
}
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::ECDSAP256SHA256 |
|
||||
Algorithm::ECDSAP384SHA384 => {
|
||||
let ec = try!(EcKey::private_key_from_pem(bytes));
|
||||
KeyPair::from_ec_key(ec)
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
Algorithm::ED25519 => {
|
||||
let mut private_key = [0u8; 32];
|
||||
let mut public_key = [0u8; 32];
|
||||
|
||||
if bytes.len() != 64 {
|
||||
return Err(DnsSecErrorKind::Msg(format!("expected 64 bytes: {}", bytes.len()))
|
||||
.into());
|
||||
}
|
||||
|
||||
private_key.copy_from_slice(&bytes[..32]);
|
||||
public_key.copy_from_slice(&bytes[32..]);
|
||||
|
||||
Ok(KeyPair::from_ed25519(Ed25519KeyPairBytes {
|
||||
private_key: private_key,
|
||||
public_key: public_key,
|
||||
}))
|
||||
}
|
||||
#[cfg(not(all(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a new private and public key pair for the specified algorithm.
|
||||
///
|
||||
/// RSA keys are hardcoded to 2048bits at the moment. Other keys have predefined sizes.
|
||||
@ -603,112 +331,132 @@ impl KeyPair {
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
Algorithm::ED25519 => {
|
||||
let rng = rand::SystemRandom::new();
|
||||
Ed25519KeyPair::generate_serializable(&rng)
|
||||
.map_err(|e| e.into())
|
||||
.map(|(_, key)| KeyPair::from_ed25519(key))
|
||||
Err(DnsSecErrorKind::Message("use generate_pkcs8 for generating private key and encoding").into())
|
||||
}
|
||||
#[cfg(not(all(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a key, securing it with pkcs8
|
||||
#[cfg(feature = "ring")]
|
||||
pub fn generate_pkcs8(algorithm: Algorithm) -> DnsSecResult<Vec<u8>> {
|
||||
match algorithm {
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::RSASHA1 |
|
||||
Algorithm::RSASHA1NSEC3SHA1 |
|
||||
Algorithm::RSASHA256 |
|
||||
Algorithm::RSASHA512 => {
|
||||
Err(DnsSecErrorKind::Message("openssl does not yet support pkcs8").into())
|
||||
}
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::ECDSAP256SHA256 => {
|
||||
Err(DnsSecErrorKind::Message("openssl does not yet support pkcs8").into())
|
||||
}
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::ECDSAP384SHA384 => {
|
||||
Err(DnsSecErrorKind::Message("openssl does not yet support pkcs8").into())
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
Algorithm::ED25519 => {
|
||||
let rng = rand::SystemRandom::new();
|
||||
Ed25519KeyPair::generate_pkcs8(&rng)
|
||||
.map_err(|e| e.into())
|
||||
.map(|pkcs8_bytes| pkcs8_bytes.to_vec())
|
||||
}
|
||||
#[cfg(not(all(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_rsa_hashing() {
|
||||
hash_test(Algorithm::RSASHA256);
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_ec_hashing_p256() {
|
||||
hash_test(Algorithm::ECDSAP256SHA256);
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_ec_hashing_p384() {
|
||||
hash_test(Algorithm::ECDSAP384SHA384);
|
||||
}
|
||||
|
||||
#[cfg(feature = "ring")]
|
||||
#[test]
|
||||
fn test_ed25519() {
|
||||
hash_test(Algorithm::ED25519);
|
||||
impl PublicKey for KeyPair {
|
||||
fn public_bytes(&self) -> &[u8] {
|
||||
// FIXME: don't actually need access to the public key bytes?
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> DnsSecResult<()> {
|
||||
match *self {
|
||||
#[cfg(feature = "openssl")]
|
||||
KeyPair::RSA(ref pkey) |
|
||||
KeyPair::EC(ref pkey) => {
|
||||
let digest_type = try!(DigestType::from(algorithm).to_openssl_digest());
|
||||
let mut verifier = Verifier::new(digest_type, &pkey).unwrap();
|
||||
try!(verifier.update(message));
|
||||
verifier
|
||||
.finish(signature)
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|b| if b {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(DnsSecErrorKind::Message("could not verify").into())
|
||||
})
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
KeyPair::ED25519(ref ed_key) => {
|
||||
let pub_key = Ed25519::from_public_bytes(&ed_key.public_key_bytes())?;
|
||||
pub_key
|
||||
.verify(algorithm, message, signature)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
_ => Err(DnsSecErrorKind::Message("openssl nor ring feature(s) not enabled").into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
#[cfg(test)]
|
||||
fn hash_test(algorithm: Algorithm) {
|
||||
let bytes = b"www.example.com";
|
||||
mod tests {
|
||||
use rr::dnssec::*;
|
||||
|
||||
let key = KeyPair::generate(algorithm).unwrap();
|
||||
let neg = KeyPair::generate(algorithm).unwrap();
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_rsa_hashing() {
|
||||
hash_test(Algorithm::RSASHA256, KeyFormat::Der);
|
||||
}
|
||||
|
||||
let sig = key.sign(algorithm, bytes).unwrap();
|
||||
assert!(key.verify(algorithm, bytes, &sig).is_ok(),
|
||||
"algorithm: {:?}",
|
||||
algorithm);
|
||||
assert!(!neg.verify(algorithm, bytes, &sig).is_ok(),
|
||||
"algorithm: {:?}",
|
||||
algorithm);
|
||||
}
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_ec_hashing_p256() {
|
||||
hash_test(Algorithm::ECDSAP256SHA256, KeyFormat::Der);
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_ec_hashing_p384() {
|
||||
hash_test(Algorithm::ECDSAP384SHA384, KeyFormat::Der);
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_to_from_public_key_rsa() {
|
||||
to_from_public_key_test(Algorithm::RSASHA256);
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
#[test]
|
||||
fn test_ed25519() {
|
||||
hash_test(Algorithm::ED25519, KeyFormat::Pkcs8);
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_to_from_public_key_ec_p256() {
|
||||
to_from_public_key_test(Algorithm::ECDSAP256SHA256);
|
||||
}
|
||||
fn hash_test(algorithm: Algorithm, key_format: KeyFormat) {
|
||||
let bytes = b"www.example.com";
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_to_from_public_key_ec_p384() {
|
||||
to_from_public_key_test(Algorithm::ECDSAP384SHA384);
|
||||
}
|
||||
// TODO: convert to stored keys...
|
||||
let key = key_format
|
||||
.decode_key(&key_format.generate_and_encode(algorithm, None).unwrap(),
|
||||
None,
|
||||
algorithm)
|
||||
.unwrap();
|
||||
let neg = key_format
|
||||
.decode_key(&key_format.generate_and_encode(algorithm, None).unwrap(),
|
||||
None,
|
||||
algorithm)
|
||||
.unwrap();
|
||||
|
||||
#[cfg(feature = "ring")]
|
||||
#[test]
|
||||
fn test_to_from_public_key_ed25519() {
|
||||
to_from_public_key_test(Algorithm::ED25519);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn to_from_public_key_test(algorithm: Algorithm) {
|
||||
let key = KeyPair::generate(algorithm).unwrap();
|
||||
|
||||
assert!(key.to_public_bytes()
|
||||
.and_then(|bytes| KeyPair::from_public_bytes(&bytes, algorithm))
|
||||
.is_ok());
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_serialization_ec() {
|
||||
test_serialization(Algorithm::ECDSAP256SHA256);
|
||||
}
|
||||
|
||||
#[cfg(feature = "ring")]
|
||||
#[test]
|
||||
fn test_serialization_ed25519() {
|
||||
test_serialization(Algorithm::ED25519);
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
#[test]
|
||||
fn test_serialization_rsa() {
|
||||
test_serialization(Algorithm::RSASHA256);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_serialization(algorithm: Algorithm) {
|
||||
let key = KeyPair::generate(algorithm).unwrap();
|
||||
|
||||
assert!(KeyPair::from_private_bytes(algorithm, &key.to_private_bytes().unwrap()).is_ok());
|
||||
}
|
||||
let sig = key.sign(algorithm, bytes).unwrap();
|
||||
assert!(key.verify(algorithm, bytes, &sig).is_ok(),
|
||||
"algorithm: {:?}",
|
||||
algorithm);
|
||||
assert!(!neg.verify(algorithm, bytes, &sig).is_ok(),
|
||||
"algorithm: {:?}",
|
||||
algorithm);
|
||||
}
|
||||
}
|
@ -18,13 +18,16 @@
|
||||
|
||||
mod algorithm;
|
||||
mod digest_type;
|
||||
pub mod hash;
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
mod key_format;
|
||||
mod keypair;
|
||||
mod nsec3;
|
||||
pub mod public_key;
|
||||
mod signer;
|
||||
mod supported_algorithm;
|
||||
mod trust_anchor;
|
||||
mod verifier;
|
||||
|
||||
pub use self::algorithm::Algorithm;
|
||||
pub use self::digest_type::DigestType;
|
||||
@ -32,9 +35,12 @@ pub use self::digest_type::DigestType;
|
||||
pub use self::key_format::KeyFormat;
|
||||
pub use self::keypair::KeyPair;
|
||||
pub use self::nsec3::Nsec3HashAlgorithm;
|
||||
pub use self::public_key::PublicKey;
|
||||
pub use self::public_key::PublicKeyEnum;
|
||||
pub use self::signer::Signer;
|
||||
pub use self::supported_algorithm::SupportedAlgorithms;
|
||||
pub use self::trust_anchor::TrustAnchor;
|
||||
pub use self::verifier::Verifier;
|
||||
|
||||
pub use error::DnsSecError;
|
||||
pub use error::DnsSecErrorKind;
|
||||
|
410
client/src/rr/dnssec/public_key.rs
Normal file
410
client/src/rr/dnssec/public_key.rs
Normal file
@ -0,0 +1,410 @@
|
||||
// Copyright 2015-2016 Benjamin Fry <benjaminfry@me.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
|
||||
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
|
||||
// http://opensource.org/licenses/MIT>, at your option. This file may not be
|
||||
// copied, modified, or distributed except according to those terms.
|
||||
|
||||
//! Public Key implementations for supported key types
|
||||
#[cfg(not(any(feature = "openssl", feature = "ring")))]
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::rsa::Rsa as OpenSslRsa;
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::sign::Verifier;
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::pkey::PKey;
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::bn::{BigNum, BigNumContext};
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::ec::{EcGroup, EcKey, EcPoint};
|
||||
#[cfg(feature = "openssl")]
|
||||
use openssl::nid;
|
||||
#[cfg(feature = "ring")]
|
||||
use ring::signature::{ED25519_PUBLIC_KEY_LEN, EdDSAParameters, VerificationAlgorithm};
|
||||
#[cfg(feature = "ring")]
|
||||
use untrusted::Input;
|
||||
|
||||
use error::*;
|
||||
#[cfg(feature = "openssl")]
|
||||
use rr::dnssec::DigestType;
|
||||
use rr::dnssec::Algorithm;
|
||||
|
||||
/// PublicKeys implement the ability to ideally be zero copy abstractions over public keys for verifying signed content.
|
||||
///
|
||||
/// In DNS the KEY and DNSKEY types are generally the RData types which store public key material.
|
||||
pub trait PublicKey {
|
||||
/// Returns the public bytes of the public key, in DNS format
|
||||
fn public_bytes(&self) -> &[u8];
|
||||
|
||||
/// Verifies the hash matches the signature with the current `key`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `message` - the message to be validated, see `hash_rrset`
|
||||
/// * `signature` - the signature to use to verify the hash, extracted from an `RData::RRSIG`
|
||||
/// for example.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// True if and only if the signature is valid for the hash. This will always return
|
||||
/// false if the `key`.
|
||||
#[allow(unused)]
|
||||
fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> DnsSecResult<()>;
|
||||
|
||||
/// The key tag is calculated as a hash to more quickly lookup a DNSKEY.
|
||||
///
|
||||
/// [RFC 1035](https://tools.ietf.org/html/rfc1035), DOMAIN NAMES - IMPLEMENTATION AND SPECIFICATION, November 1987
|
||||
///
|
||||
/// ```text
|
||||
/// RFC 2535 DNS Security Extensions March 1999
|
||||
///
|
||||
/// 4.1.6 Key Tag Field
|
||||
///
|
||||
/// The "key Tag" is a two octet quantity that is used to efficiently
|
||||
/// select between multiple keys which may be applicable and thus check
|
||||
/// that a public key about to be used for the computationally expensive
|
||||
/// effort to check the signature is possibly valid. For algorithm 1
|
||||
/// (MD5/RSA) as defined in [RFC 2537], it is the next to the bottom two
|
||||
/// octets of the public key modulus needed to decode the signature
|
||||
/// field. That is to say, the most significant 16 of the least
|
||||
/// significant 24 bits of the modulus in network (big endian) order. For
|
||||
/// all other algorithms, including private algorithms, it is calculated
|
||||
/// as a simple checksum of the KEY RR as described in Appendix C.
|
||||
///
|
||||
/// Appendix C: Key Tag Calculation
|
||||
///
|
||||
/// The key tag field in the SIG RR is just a means of more efficiently
|
||||
/// selecting the correct KEY RR to use when there is more than one KEY
|
||||
/// RR candidate available, for example, in verifying a signature. It is
|
||||
/// possible for more than one candidate key to have the same tag, in
|
||||
/// which case each must be tried until one works or all fail. The
|
||||
/// following reference implementation of how to calculate the Key Tag,
|
||||
/// for all algorithms other than algorithm 1, is in ANSI C. It is coded
|
||||
/// for clarity, not efficiency. (See section 4.1.6 for how to determine
|
||||
/// the Key Tag of an algorithm 1 key.)
|
||||
///
|
||||
/// /* assumes int is at least 16 bits
|
||||
/// first byte of the key tag is the most significant byte of return
|
||||
/// value
|
||||
/// second byte of the key tag is the least significant byte of
|
||||
/// return value
|
||||
/// */
|
||||
///
|
||||
/// int keytag (
|
||||
///
|
||||
/// unsigned char key[], /* the RDATA part of the KEY RR */
|
||||
/// unsigned int keysize, /* the RDLENGTH */
|
||||
/// )
|
||||
/// {
|
||||
/// long int ac; /* assumed to be 32 bits or larger */
|
||||
///
|
||||
/// for ( ac = 0, i = 0; i < keysize; ++i )
|
||||
/// ac += (i&1) ? key[i] : key[i]<<8;
|
||||
/// ac += (ac>>16) & 0xFFFF;
|
||||
/// return ac & 0xFFFF;
|
||||
/// }
|
||||
/// ```
|
||||
fn key_tag(&self) -> DnsSecResult<u16> {
|
||||
let mut ac: usize = 0;
|
||||
|
||||
for (i, k) in self.public_bytes().iter().enumerate() {
|
||||
ac += if i & 0x0001 == 0x0001 {
|
||||
*k as usize
|
||||
} else {
|
||||
(*k as usize) << 8
|
||||
};
|
||||
}
|
||||
|
||||
ac += (ac >> 16) & 0xFFFF;
|
||||
return Ok((ac & 0xFFFF) as u16);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
fn verify_with_pkey(pkey: &PKey,
|
||||
algorithm: Algorithm,
|
||||
message: &[u8],
|
||||
signature: &[u8])
|
||||
-> DnsSecResult<()> {
|
||||
let digest_type = try!(DigestType::from(algorithm).to_openssl_digest());
|
||||
let mut verifier = Verifier::new(digest_type, &pkey).unwrap();
|
||||
try!(verifier.update(message));
|
||||
verifier
|
||||
.finish(signature)
|
||||
.map_err(|e| e.into())
|
||||
.and_then(|b| if b {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(DnsSecErrorKind::Message("could not verify").into())
|
||||
})
|
||||
}
|
||||
|
||||
/// Elyptic Curve public key type
|
||||
#[cfg(feature = "openssl")]
|
||||
pub struct Ec<'k> {
|
||||
raw: &'k [u8],
|
||||
pkey: PKey,
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
impl<'k> Ec<'k> {
|
||||
/// ```text
|
||||
/// RFC 6605 ECDSA for DNSSEC April 2012
|
||||
///
|
||||
/// 4. DNSKEY and RRSIG Resource Records for ECDSA
|
||||
///
|
||||
/// ECDSA public keys consist of a single value, called "Q" in FIPS
|
||||
/// 186-3. In DNSSEC keys, Q is a simple bit string that represents the
|
||||
/// uncompressed form of a curve point, "x | y".
|
||||
///
|
||||
/// The ECDSA signature is the combination of two non-negative integers,
|
||||
/// called "r" and "s" in FIPS 186-3. The two integers, each of which is
|
||||
/// formatted as a simple octet string, are combined into a single longer
|
||||
/// octet string for DNSSEC as the concatenation "r | s". (Conversion of
|
||||
/// the integers to bit strings is described in Section C.2 of FIPS
|
||||
/// 186-3.) For P-256, each integer MUST be encoded as 32 octets; for
|
||||
/// P-384, each integer MUST be encoded as 48 octets.
|
||||
///
|
||||
/// The algorithm numbers associated with the DNSKEY and RRSIG resource
|
||||
/// records are fully defined in the IANA Considerations section. They
|
||||
/// are:
|
||||
///
|
||||
/// o DNSKEY and RRSIG RRs signifying ECDSA with the P-256 curve and
|
||||
/// SHA-256 use the algorithm number 13.
|
||||
///
|
||||
/// o DNSKEY and RRSIG RRs signifying ECDSA with the P-384 curve and
|
||||
/// SHA-384 use the algorithm number 14.
|
||||
///
|
||||
/// Conformant implementations that create records to be put into the DNS
|
||||
/// MUST implement signing and verification for both of the above
|
||||
/// algorithms. Conformant DNSSEC verifiers MUST implement verification
|
||||
/// for both of the above algorithms.
|
||||
/// ```
|
||||
pub fn from_public_bytes(public_key: &'k [u8], algorithm: Algorithm) -> DnsSecResult<Self> {
|
||||
let curve = match algorithm {
|
||||
Algorithm::ECDSAP256SHA256 => nid::SECP256K1,
|
||||
Algorithm::ECDSAP384SHA384 => nid::SECP384R1,
|
||||
_ => return Err("only ECDSAP256SHA256 and ECDSAP384SHA384 are supported by Ec".into()),
|
||||
};
|
||||
|
||||
EcGroup::from_curve_name(curve)
|
||||
.and_then(|group| BigNumContext::new().map(|ctx| (group, ctx)))
|
||||
// FYI: BigNum slices treat all slices as BigEndian, i.e NetworkByteOrder
|
||||
.and_then(|(group, mut ctx)| EcPoint::from_bytes(&group, public_key, &mut ctx).map(|point| (group, point) ))
|
||||
.and_then(|(group, point)| EcKey::from_public_key(&group, &point))
|
||||
.and_then(|ec_key| PKey::from_ec_key(ec_key) )
|
||||
.map_err(|e| e.into())
|
||||
.map(|pkey| Ec{raw: public_key, pkey: pkey})
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
impl<'k> PublicKey for Ec<'k> {
|
||||
fn public_bytes(&self) -> &[u8] {
|
||||
self.raw
|
||||
}
|
||||
|
||||
fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> DnsSecResult<()> {
|
||||
verify_with_pkey(&self.pkey, algorithm, message, signature)
|
||||
}
|
||||
}
|
||||
|
||||
/// Ed25519 Public key
|
||||
#[cfg(feature = "ring")]
|
||||
pub struct Ed25519<'k> {
|
||||
raw: &'k [u8],
|
||||
}
|
||||
|
||||
#[cfg(feature = "ring")]
|
||||
impl<'k> Ed25519<'k> {
|
||||
/// ```text
|
||||
/// Internet-Draft EdDSA for DNSSEC December 2016
|
||||
///
|
||||
/// An Ed25519 public key consists of a 32-octet value, which is encoded
|
||||
/// into the Public Key field of a DNSKEY resource record as a simple bit
|
||||
/// string. The generation of a public key is defined in Section 5.1.5
|
||||
/// in [RFC 8032]. Breaking tradition, the keys are encoded in little-
|
||||
/// endian byte order.
|
||||
/// ```
|
||||
pub fn from_public_bytes(public_key: &'k [u8]) -> DnsSecResult<Self> {
|
||||
if public_key.len() != ED25519_PUBLIC_KEY_LEN {
|
||||
return Err(DnsSecErrorKind::Msg(format!("expected {} byte public_key: {}",
|
||||
ED25519_PUBLIC_KEY_LEN,
|
||||
public_key.len()))
|
||||
.into());
|
||||
}
|
||||
|
||||
Ok(Ed25519 { raw: public_key })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ring")]
|
||||
impl<'k> PublicKey for Ed25519<'k> {
|
||||
// TODO: just store reference to public key bytes in ctor...
|
||||
fn public_bytes(&self) -> &[u8] {
|
||||
self.raw
|
||||
}
|
||||
|
||||
fn verify(&self, _: Algorithm, message: &[u8], signature: &[u8]) -> DnsSecResult<()> {
|
||||
let public_key = Input::from(self.raw);
|
||||
let message = Input::from(message);
|
||||
let signature = Input::from(signature);
|
||||
EdDSAParameters {}
|
||||
.verify(public_key, message, signature)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Rsa public key
|
||||
#[cfg(feature = "openssl")]
|
||||
pub struct Rsa<'k> {
|
||||
raw: &'k [u8],
|
||||
pkey: PKey,
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
impl<'k> Rsa<'k> {
|
||||
/// ```text
|
||||
/// RFC 3110 RSA SIGs and KEYs in the DNS May 2001
|
||||
///
|
||||
/// 2. RSA Public KEY Resource Records
|
||||
///
|
||||
/// RSA public keys are stored in the DNS as KEY RRs using algorithm
|
||||
/// number 5 [RFC2535]. The structure of the algorithm specific portion
|
||||
/// of the RDATA part of such RRs is as shown below.
|
||||
///
|
||||
/// Field Size
|
||||
/// ----- ----
|
||||
/// exponent length 1 or 3 octets (see text)
|
||||
/// exponent as specified by length field
|
||||
/// modulus remaining space
|
||||
///
|
||||
/// For interoperability, the exponent and modulus are each limited to
|
||||
/// 4096 bits in length. The public key exponent is a variable length
|
||||
/// unsigned integer. Its length in octets is represented as one octet
|
||||
/// if it is in the range of 1 to 255 and by a zero octet followed by a
|
||||
/// two octet unsigned length if it is longer than 255 bytes. The public
|
||||
/// key modulus field is a multiprecision unsigned integer. The length
|
||||
/// of the modulus can be determined from the RDLENGTH and the preceding
|
||||
/// RDATA fields including the exponent. Leading zero octets are
|
||||
/// prohibited in the exponent and modulus.
|
||||
///
|
||||
/// Note: KEY RRs for use with RSA/SHA1 DNS signatures MUST use this
|
||||
/// algorithm number (rather than the algorithm number specified in the
|
||||
/// obsoleted RFC 2537).
|
||||
///
|
||||
/// Note: This changes the algorithm number for RSA KEY RRs to be the
|
||||
/// same as the new algorithm number for RSA/SHA1 SIGs.
|
||||
/// ```
|
||||
pub fn from_public_bytes(public_key: &'k [u8]) -> DnsSecResult<Self> {
|
||||
if public_key.len() < 3 || public_key.len() > (4096 + 3) {
|
||||
return Err(DnsSecErrorKind::Message("bad public key").into());
|
||||
}
|
||||
let mut num_exp_len_octs = 1;
|
||||
let mut len: u16 = public_key[0] as u16;
|
||||
if len == 0 {
|
||||
num_exp_len_octs = 3;
|
||||
len = ((public_key[1] as u16) << 8) | (public_key[2] as u16)
|
||||
}
|
||||
let len = len; // demut
|
||||
|
||||
// FYI: BigNum slices treat all slices as BigEndian, i.e NetworkByteOrder
|
||||
let e = try!(BigNum::from_slice(&public_key[(num_exp_len_octs as usize)..
|
||||
(len as usize + num_exp_len_octs)]));
|
||||
let n = try!(BigNum::from_slice(&public_key[(len as usize + num_exp_len_octs)..]));
|
||||
|
||||
OpenSslRsa::from_public_components(n, e)
|
||||
.and_then(|rsa| PKey::from_rsa(rsa))
|
||||
.map_err(|e| e.into())
|
||||
.map(|pkey| {
|
||||
Rsa {
|
||||
raw: public_key,
|
||||
pkey: pkey,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "openssl")]
|
||||
impl<'k> PublicKey for Rsa<'k> {
|
||||
fn public_bytes(&self) -> &[u8] {
|
||||
self.raw
|
||||
}
|
||||
|
||||
fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> DnsSecResult<()> {
|
||||
verify_with_pkey(&self.pkey, algorithm, message, signature)
|
||||
}
|
||||
}
|
||||
|
||||
/// Variants of all know public keys
|
||||
pub enum PublicKeyEnum<'k> {
|
||||
/// RSA keypair, supported by OpenSSL
|
||||
#[cfg(feature = "openssl")]
|
||||
Rsa(Rsa<'k>),
|
||||
/// Ellyptic curve keypair, supported by OpenSSL
|
||||
#[cfg(feature = "openssl")]
|
||||
Ec(Ec<'k>),
|
||||
/// Ed25519 public key for the Algorithm::ED25519
|
||||
#[cfg(feature = "ring")]
|
||||
Ed25519(Ed25519<'k>),
|
||||
/// PhatomData for compiler when ring and or openssl not defined, do not use...
|
||||
#[cfg(not(any(feature = "ring", feature = "openssl")))]
|
||||
Phantom(&'k PhantomData<()>),
|
||||
}
|
||||
|
||||
impl<'k> PublicKeyEnum<'k> {
|
||||
/// Converts the bytes into a PulbicKey of the specified algorithm
|
||||
#[allow(unused_variables)]
|
||||
pub fn from_public_bytes(public_key: &'k [u8], algorithm: Algorithm) -> DnsSecResult<Self> {
|
||||
match algorithm {
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::ECDSAP256SHA256 |
|
||||
Algorithm::ECDSAP384SHA384 => {
|
||||
Ok(PublicKeyEnum::Ec(Ec::from_public_bytes(public_key, algorithm)?))
|
||||
}
|
||||
#[cfg(feature = "ring")]
|
||||
Algorithm::ED25519 => {
|
||||
Ok(PublicKeyEnum::Ed25519(Ed25519::from_public_bytes(public_key)?))
|
||||
}
|
||||
#[cfg(feature = "openssl")]
|
||||
Algorithm::RSASHA1 |
|
||||
Algorithm::RSASHA1NSEC3SHA1 |
|
||||
Algorithm::RSASHA256 |
|
||||
Algorithm::RSASHA512 => Ok(PublicKeyEnum::Rsa(Rsa::from_public_bytes(public_key)?)),
|
||||
#[cfg(not(all(feature = "ring", feature = "openssl")))]
|
||||
_ => Err("no public keys registered, enable ring or openssl features".into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'k> PublicKey for PublicKeyEnum<'k> {
|
||||
fn public_bytes(&self) -> &[u8] {
|
||||
match *self {
|
||||
#[cfg(feature = "openssl")]
|
||||
PublicKeyEnum::Ec(ref ec) => ec.public_bytes(),
|
||||
#[cfg(feature = "ring")]
|
||||
PublicKeyEnum::Ed25519(ref ed) => ed.public_bytes(),
|
||||
#[cfg(feature = "openssl")]
|
||||
PublicKeyEnum::Rsa(ref rsa) => rsa.public_bytes(),
|
||||
#[cfg(not(any(feature = "ring", feature = "openssl")))]
|
||||
_ => panic!("no public keys registered, enable ring or openssl features"),
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_variables)]
|
||||
fn verify(&self, algorithm: Algorithm, message: &[u8], signature: &[u8]) -> DnsSecResult<()> {
|
||||
match *self {
|
||||
#[cfg(feature = "openssl")]
|
||||
PublicKeyEnum::Ec(ref ec) => ec.verify(algorithm, message, signature),
|
||||
#[cfg(feature = "ring")]
|
||||
PublicKeyEnum::Ed25519(ref ed) => ed.verify(algorithm, message, signature),
|
||||
#[cfg(feature = "openssl")]
|
||||
PublicKeyEnum::Rsa(ref rsa) => rsa.verify(algorithm, message, signature),
|
||||
#[cfg(not(any(feature = "ring", feature = "openssl")))]
|
||||
_ => panic!("no public keys registered, enable ring or openssl features"),
|
||||
}
|
||||
}
|
||||
}
|
@ -21,13 +21,15 @@ use chrono::Duration;
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
use op::Message;
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
use rr::{DNSClass, Name, Record, RecordType, RData};
|
||||
use rr::{Name, RData};
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
use rr::dnssec::{Algorithm, DnsSecErrorKind, DnsSecResult, KeyPair};
|
||||
use rr::dnssec::KeyPair;
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
use rr::rdata::{DNSKEY, KEY, sig, SIG};
|
||||
use rr::dnssec::{Algorithm, DnsSecResult, hash, PublicKey, PublicKeyEnum, Verifier};
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
use serialize::binary::{BinEncoder, BinSerializable, EncodeMode};
|
||||
use rr::rdata::{DNSKEY, KEY, SIG};
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
use serialize::binary::BinEncoder;
|
||||
|
||||
/// Use for performing signing and validation of DNSSec based components.
|
||||
///
|
||||
@ -261,7 +263,7 @@ impl Signer {
|
||||
signer_name: Name,
|
||||
sig_duration: Duration)
|
||||
-> Self {
|
||||
let algorithm = *key_rdata.algorithm();
|
||||
let algorithm = key_rdata.algorithm();
|
||||
let is_zone_signing_key = key_rdata.zone_key();
|
||||
|
||||
Signer {
|
||||
@ -283,7 +285,7 @@ impl Signer {
|
||||
/// * `signer_name` - name in the zone to which this DNSKEY is bound
|
||||
/// * `is_zone_update_auth` - this key may be used for updating the zone
|
||||
pub fn sig0(key_rdata: KEY, key: KeyPair, signer_name: Name) -> Self {
|
||||
let algorithm = *key_rdata.algorithm();
|
||||
let algorithm = key_rdata.algorithm();
|
||||
|
||||
Signer {
|
||||
key_rdata: key_rdata.into(),
|
||||
@ -420,37 +422,6 @@ impl Signer {
|
||||
return Ok((ac & 0xFFFF) as u16); // this is unnecessary, no?
|
||||
}
|
||||
|
||||
fn hash_message(&self, message: &Message, pre_sig0: &SIG) -> DnsSecResult<Vec<u8>> {
|
||||
// TODO: should perform the serialization and sign block by block to reduce the max memory
|
||||
// usage, though at 4k max, this is probably unnecessary... For AXFR and large zones, it's
|
||||
// more important
|
||||
let mut buf: Vec<u8> = Vec::with_capacity(512);
|
||||
let mut buf2: Vec<u8> = Vec::with_capacity(512);
|
||||
|
||||
{
|
||||
let mut encoder: BinEncoder = BinEncoder::with_mode(&mut buf, EncodeMode::Normal);
|
||||
assert!(sig::emit_pre_sig(&mut encoder,
|
||||
pre_sig0.type_covered(),
|
||||
pre_sig0.algorithm(),
|
||||
pre_sig0.num_labels(),
|
||||
pre_sig0.original_ttl(),
|
||||
pre_sig0.sig_expiration(),
|
||||
pre_sig0.sig_inception(),
|
||||
pre_sig0.key_tag(),
|
||||
pre_sig0.signer_name())
|
||||
.is_ok());
|
||||
// need a separate encoder here, as the encoding references absolute positions
|
||||
// inside the buffer. If the buffer already contains the sig0 RDATA, offsets
|
||||
// are wrong and the signature won't match.
|
||||
let mut encoder2: BinEncoder = BinEncoder::with_mode(&mut buf2, EncodeMode::Signing);
|
||||
message.emit(&mut encoder2).unwrap(); // coding error if this panics (i think?)
|
||||
}
|
||||
|
||||
buf.append(&mut buf2);
|
||||
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// Signs the given message, returning the signature bytes.
|
||||
///
|
||||
/// # Arguments
|
||||
@ -507,232 +478,7 @@ impl Signer {
|
||||
///
|
||||
/// ---
|
||||
pub fn sign_message(&self, message: &Message, pre_sig0: &SIG) -> DnsSecResult<Vec<u8>> {
|
||||
self.hash_message(message, pre_sig0)
|
||||
.and_then(|hash| self.sign(&hash))
|
||||
}
|
||||
|
||||
/// Verifies a message with the against the given signature
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `message` - the message to verify
|
||||
/// * `signature` - the signature to use for validation
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// `true` if the message could be validated against the signature, `false` otherwise
|
||||
pub fn verify_message(&self,
|
||||
message: &Message,
|
||||
signature: &[u8],
|
||||
sig0: &SIG)
|
||||
-> DnsSecResult<()> {
|
||||
self.hash_message(message, sig0)
|
||||
.and_then(|hash| self.verify(&hash, signature))
|
||||
}
|
||||
|
||||
/// Computes the hash of the given record set
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - RRset record name
|
||||
/// * `dns_class` - DNSClass, i.e. IN, of the records
|
||||
/// * `num_labels` - number of labels in the name, needed to deal with `*.example.com`
|
||||
/// * `type_covered` - RecordType of the RRSet being hashed
|
||||
/// * `algorithm` - The Algorithm type used for the hashing
|
||||
/// * `original_ttl` - Original TTL is the TTL as specified in the SOA zones RRSet associated record
|
||||
/// * `sig_expiration` - the epoch seconds of when this hashed signature will expire
|
||||
/// * `key_inception` - the epoch seconds of when this hashed signature will be valid
|
||||
/// * `signer_name` - label of the etity responsible for signing this hash
|
||||
/// * `records` - RRSet to hash
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// the binary hash of the specified RRSet and associated information
|
||||
pub fn hash_rrset(&self,
|
||||
name: &Name,
|
||||
dns_class: DNSClass,
|
||||
num_labels: u8,
|
||||
type_covered: RecordType,
|
||||
algorithm: Algorithm,
|
||||
original_ttl: u32,
|
||||
sig_expiration: u32,
|
||||
sig_inception: u32,
|
||||
key_tag: u16,
|
||||
signer_name: &Name,
|
||||
records: &[Record])
|
||||
-> DnsSecResult<Vec<u8>> {
|
||||
// TODO: change this to a BTreeSet so that it's preordered, no sort necessary
|
||||
let mut rrset: Vec<&Record> = Vec::new();
|
||||
|
||||
// collect only the records for this rrset
|
||||
for record in records {
|
||||
if dns_class == record.dns_class() && type_covered == record.rr_type() &&
|
||||
name == record.name() {
|
||||
rrset.push(record);
|
||||
}
|
||||
}
|
||||
|
||||
// put records in canonical order
|
||||
rrset.sort();
|
||||
|
||||
let name: Name = if let Some(name) = Self::determine_name(name, num_labels) {
|
||||
name
|
||||
} else {
|
||||
return Err(DnsSecErrorKind::Msg(format!("could not determine name from {}", name))
|
||||
.into());
|
||||
};
|
||||
|
||||
// TODO: rather than buffering here, use the Signer/Verifier? might mean fewer allocations...
|
||||
let mut buf: Vec<u8> = Vec::new();
|
||||
|
||||
{
|
||||
let mut encoder: BinEncoder = BinEncoder::new(&mut buf);
|
||||
encoder.set_canonical_names(true);
|
||||
|
||||
// signed_data = RRSIG_RDATA | RR(1) | RR(2)... where
|
||||
//
|
||||
// "|" denotes concatenation
|
||||
//
|
||||
// RRSIG_RDATA is the wire format of the RRSIG RDATA fields
|
||||
// with the Signature field excluded and the Signer's Name
|
||||
// in canonical form.
|
||||
assert!(sig::emit_pre_sig(&mut encoder,
|
||||
type_covered,
|
||||
algorithm,
|
||||
name.num_labels(),
|
||||
original_ttl,
|
||||
sig_expiration,
|
||||
sig_inception,
|
||||
key_tag,
|
||||
&signer_name)
|
||||
.is_ok());
|
||||
|
||||
// construct the rrset signing data
|
||||
for record in rrset {
|
||||
// RR(i) = name | type | class | OrigTTL | RDATA length | RDATA
|
||||
//
|
||||
// name is calculated according to the function in the RFC 4035
|
||||
assert!(name.to_lowercase()
|
||||
.emit_as_canonical(&mut encoder, true)
|
||||
.is_ok());
|
||||
//
|
||||
// type is the RRset type and all RRs in the class
|
||||
assert!(type_covered.emit(&mut encoder).is_ok());
|
||||
//
|
||||
// class is the RRset's class
|
||||
assert!(dns_class.emit(&mut encoder).is_ok());
|
||||
//
|
||||
// OrigTTL is the value from the RRSIG Original TTL field
|
||||
assert!(encoder.emit_u32(original_ttl).is_ok());
|
||||
//
|
||||
// RDATA length
|
||||
// TODO: add support to the encoder to set a marker to go back and write the length
|
||||
let mut rdata_buf = Vec::new();
|
||||
{
|
||||
let mut rdata_encoder = BinEncoder::new(&mut rdata_buf);
|
||||
rdata_encoder.set_canonical_names(true);
|
||||
assert!(record.rdata().emit(&mut rdata_encoder).is_ok());
|
||||
}
|
||||
assert!(encoder.emit_u16(rdata_buf.len() as u16).is_ok());
|
||||
//
|
||||
// All names in the RDATA field are in canonical form (set above)
|
||||
assert!(encoder.emit_vec(&rdata_buf).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: This used to return the hash, now it's a hashable record type?
|
||||
// DigestType::from(self.algorithm).hash(&buf)
|
||||
Ok(buf)
|
||||
}
|
||||
|
||||
/// hashes the RRSet with information provided from the RRSig record
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `rrsig` - SIG or RRSIG record, which was produced from the RRSet
|
||||
/// * `records` - RRSet records to sign with the information in the `rrsig`
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// binary hash of the RRSet with the information from the RRSIG record
|
||||
pub fn hash_rrset_with_rrsig(&self,
|
||||
rrsig: &Record,
|
||||
records: &[Record])
|
||||
-> DnsSecResult<Vec<u8>> {
|
||||
if let &RData::SIG(ref sig) = rrsig.rdata() {
|
||||
self.hash_rrset_with_sig(rrsig.name(), rrsig.dns_class(), sig, records)
|
||||
} else {
|
||||
return Err(DnsSecErrorKind::Msg(format!("could not determine name from {}",
|
||||
rrsig.name()))
|
||||
.into());
|
||||
}
|
||||
}
|
||||
|
||||
/// hashes the RRSet with information provided from the RRSig record
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - labels of the record to sign
|
||||
/// * `dns_class` - DNSClass of the RRSet, i.e. IN
|
||||
/// * `sig` - SIG or RRSIG record, which was produced from the RRSet
|
||||
/// * `records` - RRSet records to sign with the information in the `rrsig`
|
||||
///
|
||||
/// # Return
|
||||
///
|
||||
/// binary hash of the RRSet with the information from the RRSIG record
|
||||
pub fn hash_rrset_with_sig(&self,
|
||||
name: &Name,
|
||||
dns_class: DNSClass,
|
||||
sig: &SIG,
|
||||
records: &[Record])
|
||||
-> DnsSecResult<Vec<u8>> {
|
||||
self.hash_rrset(name,
|
||||
dns_class,
|
||||
sig.num_labels(),
|
||||
sig.type_covered(),
|
||||
sig.algorithm(),
|
||||
sig.original_ttl(),
|
||||
sig.sig_expiration(),
|
||||
sig.sig_inception(),
|
||||
sig.key_tag(),
|
||||
sig.signer_name(),
|
||||
records)
|
||||
}
|
||||
|
||||
fn determine_name(name: &Name, num_labels: u8) -> Option<Name> {
|
||||
// To calculate the name:
|
||||
// let rrsig_labels = the value of the RRSIG Labels field
|
||||
//
|
||||
// let fqdn = RRset's fully qualified domain name in
|
||||
// canonical form
|
||||
//
|
||||
// let fqdn_labels = Label count of the fqdn above.
|
||||
let fqdn_labels = name.num_labels();
|
||||
// if rrsig_labels = fqdn_labels,
|
||||
// name = fqdn
|
||||
|
||||
if fqdn_labels == num_labels {
|
||||
return Some(name.clone());
|
||||
}
|
||||
// if rrsig_labels < fqdn_labels,
|
||||
// name = "*." | the rightmost rrsig_label labels of the
|
||||
// fqdn
|
||||
if num_labels < fqdn_labels {
|
||||
let mut star_name: Name = Name::new().label("*");
|
||||
let rightmost = name.trim_to(num_labels as usize);
|
||||
if !rightmost.is_root() {
|
||||
star_name.append(&rightmost);
|
||||
return Some(star_name);
|
||||
}
|
||||
return Some(star_name);
|
||||
}
|
||||
//
|
||||
// if rrsig_labels > fqdn_labels
|
||||
// the RRSIG RR did not pass the necessary validation
|
||||
// checks and MUST NOT be used to authenticate this
|
||||
// RRset.
|
||||
// TODO: this should be an error
|
||||
None
|
||||
hash::hash_message(message, pre_sig0).and_then(|hash| self.sign(&hash))
|
||||
}
|
||||
|
||||
/// Signs a hash.
|
||||
@ -751,23 +497,20 @@ impl Signer {
|
||||
.sign(self.algorithm, &hash)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies the hash matches the signature with the current `key`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `hash` - the hash to be validated, see `hash_rrset`
|
||||
/// * `signature` - the signature to use to verify the hash, extracted from an `RData::RRSIG`
|
||||
/// for example.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// True if and only if the signature is valid for the hash. This will always return
|
||||
/// false if the `key`.
|
||||
pub fn verify(&self, hash: &[u8], signature: &[u8]) -> DnsSecResult<()> {
|
||||
self.key
|
||||
.verify(self.algorithm, hash, signature)
|
||||
.map_err(|e| e.into())
|
||||
#[cfg(any(feature = "openssl", feature = "ring"))]
|
||||
impl Verifier for Signer {
|
||||
fn algorithm(&self) -> Algorithm {
|
||||
self.algorithm()
|
||||
}
|
||||
|
||||
fn key<'k>(&'k self) -> DnsSecResult<PublicKeyEnum<'k>> {
|
||||
panic!("Signer is cheating by implementing verify() directly to avoid cloning keys")
|
||||
}
|
||||
|
||||
fn verify(&self, hash: &[u8], signature: &[u8]) -> DnsSecResult<()> {
|
||||
self.key().verify(self.algorithm(), hash, signature)
|
||||
}
|
||||
}
|
||||
|
||||
@ -777,8 +520,9 @@ mod tests {
|
||||
extern crate openssl;
|
||||
use self::openssl::rsa::Rsa;
|
||||
|
||||
use rr::{Name, RecordType};
|
||||
use rr::{DNSClass, Name, Record, RecordType};
|
||||
use rr::rdata::SIG;
|
||||
use rr::dnssec::Verifier;
|
||||
use op::{Message, Query, UpdateMessage};
|
||||
|
||||
pub use super::*;
|
||||
@ -834,91 +578,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_hash_rrset() {
|
||||
let rsa = Rsa::generate(512).unwrap();
|
||||
let key = KeyPair::from_rsa(rsa).unwrap();
|
||||
let sig0key = key.to_sig0key(Algorithm::RSASHA256).unwrap();
|
||||
let signer = Signer::sig0(sig0key, key, Name::root());
|
||||
|
||||
let origin: Name = Name::parse("example.com.", None).unwrap();
|
||||
let rrsig = Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::SIG(SIG::new(RecordType::NS,
|
||||
Algorithm::RSASHA256,
|
||||
origin.num_labels(),
|
||||
86400,
|
||||
5,
|
||||
0,
|
||||
signer.calculate_key_tag().unwrap(),
|
||||
origin.clone(),
|
||||
vec![])))
|
||||
.clone();
|
||||
let rrset =
|
||||
vec![Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()))
|
||||
.clone(),
|
||||
Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()))
|
||||
.clone()];
|
||||
|
||||
let hash = signer.hash_rrset_with_rrsig(&rrsig, &rrset).unwrap();
|
||||
assert!(!hash.is_empty());
|
||||
|
||||
let rrset =
|
||||
vec![Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::CNAME)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::CNAME(Name::parse("a.iana-servers.net.", None)
|
||||
.unwrap()))
|
||||
.clone(), // different type
|
||||
Record::new()
|
||||
.set_name(Name::parse("www.example.com.", None).unwrap())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()))
|
||||
.clone(), // different name
|
||||
Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::CH)
|
||||
.set_rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()))
|
||||
.clone(), // different class
|
||||
Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("a.iana-servers.net.", None).unwrap()))
|
||||
.clone(),
|
||||
Record::new()
|
||||
.set_name(origin.clone())
|
||||
.set_ttl(86400)
|
||||
.set_rr_type(RecordType::NS)
|
||||
.set_dns_class(DNSClass::IN)
|
||||
.set_rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()))
|
||||
.clone()];
|
||||
|
||||
let filtered_hash = signer.hash_rrset_with_rrsig(&rrsig, &rrset).unwrap();
|
||||
assert!(!filtered_hash.is_empty());
|
||||
assert_eq!(hash, filtered_hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_and_verify_rrset() {
|
||||
let rsa = Rsa::generate(512).unwrap();
|
||||
@ -958,7 +617,7 @@ mod tests {
|
||||
.set_rdata(RData::NS(Name::parse("b.iana-servers.net.", None).unwrap()))
|
||||
.clone()];
|
||||
|
||||
let hash = signer.hash_rrset_with_rrsig(&rrsig, &rrset).unwrap();
|
||||
let hash = hash::hash_rrset_with_rrsig(&rrsig, &rrset).unwrap();
|
||||
let sig = signer.sign(&hash).unwrap();
|
||||
|
||||
assert!(signer.verify(&hash, &sig).is_ok());
|
||||
|
84
client/src/rr/dnssec/verifier.rs
Normal file
84
client/src/rr/dnssec/verifier.rs
Normal file
@ -0,0 +1,84 @@
|
||||
//! Verifier is a structure for performing many of the signing processes of the DNSSec specification
|
||||
|
||||
use op::Message;
|
||||
use rr::{DNSClass, Name, Record};
|
||||
use rr::dnssec::{Algorithm, DnsSecResult};
|
||||
use rr::dnssec::{hash, PublicKey, PublicKeyEnum};
|
||||
use rr::rdata::{DNSKEY, KEY, SIG};
|
||||
|
||||
/// Types which are able to verify DNS based signatures
|
||||
pub trait Verifier {
|
||||
/// Return the algorithm which this Verifier covers
|
||||
fn algorithm(&self) -> Algorithm;
|
||||
|
||||
/// Return the public key associated with this verifier
|
||||
fn key<'k>(&'k self) -> DnsSecResult<PublicKeyEnum<'k>>;
|
||||
|
||||
/// Verifies the hash matches the signature with the current `key`.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `hash` - the hash to be validated, see `hash_rrset`
|
||||
/// * `signature` - the signature to use to verify the hash, extracted from an `RData::RRSIG`
|
||||
/// for example.
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// True if and only if the signature is valid for the hash.
|
||||
/// false if the `key`.
|
||||
fn verify(&self, hash: &[u8], signature: &[u8]) -> DnsSecResult<()> {
|
||||
self.key()?.verify(self.algorithm(), hash, signature)
|
||||
}
|
||||
|
||||
/// Verifies a message with the against the given signature, i.e. SIG0
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `message` - the message to verify
|
||||
/// * `signature` - the signature to use for validation
|
||||
///
|
||||
/// # Return value
|
||||
///
|
||||
/// `true` if the message could be validated against the signature, `false` otherwise
|
||||
fn verify_message(&self, message: &Message, signature: &[u8], sig0: &SIG) -> DnsSecResult<()> {
|
||||
hash::hash_message(message, sig0).and_then(|hash| self.verify(&hash, signature))
|
||||
}
|
||||
|
||||
/// Verifies an RRSig with the associated key, e.g. DNSKEY
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `name` - name associated with the rrsig being validated
|
||||
/// * `dns_class` - DNSClass of the records, generally IN
|
||||
/// * `sig` - signature record being validated
|
||||
/// * `records` - Records covered by SIG
|
||||
fn verify_rrsig(&self,
|
||||
name: &Name,
|
||||
dns_class: DNSClass,
|
||||
sig: &SIG,
|
||||
records: &[Record])
|
||||
-> DnsSecResult<()> {
|
||||
let rrset_hash = hash::hash_rrset_with_sig(name, dns_class, sig, records)?;
|
||||
self.verify(&rrset_hash, sig.sig())
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier for DNSKEY {
|
||||
fn algorithm(&self) -> Algorithm {
|
||||
self.algorithm()
|
||||
}
|
||||
|
||||
fn key<'k>(&'k self) -> DnsSecResult<PublicKeyEnum<'k>> {
|
||||
PublicKeyEnum::from_public_bytes(self.public_key(), self.algorithm())
|
||||
}
|
||||
}
|
||||
|
||||
impl Verifier for KEY {
|
||||
fn algorithm(&self) -> Algorithm {
|
||||
self.algorithm()
|
||||
}
|
||||
|
||||
fn key<'k>(&'k self) -> DnsSecResult<PublicKeyEnum<'k>> {
|
||||
PublicKeyEnum::from_public_bytes(self.public_key(), self.algorithm())
|
||||
}
|
||||
}
|
@ -172,8 +172,8 @@ impl DNSKEY {
|
||||
/// algorithm and determines the format of the Public Key field. A list
|
||||
/// of DNSSEC algorithm types can be found in Appendix A.1
|
||||
/// ```
|
||||
pub fn algorithm(&self) -> &Algorithm {
|
||||
&self.algorithm
|
||||
pub fn algorithm(&self) -> Algorithm {
|
||||
self.algorithm
|
||||
}
|
||||
|
||||
/// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.4)
|
||||
|
@ -690,8 +690,8 @@ impl KEY {
|
||||
/// algorithm and determines the format of the Public Key field. A list
|
||||
/// of DNSSEC algorithm types can be found in Appendix A.1
|
||||
/// ```
|
||||
pub fn algorithm(&self) -> &Algorithm {
|
||||
&self.algorithm
|
||||
pub fn algorithm(&self) -> Algorithm {
|
||||
self.algorithm
|
||||
}
|
||||
|
||||
/// [RFC 4034, DNSSEC Resource Records, March 2005](https://tools.ietf.org/html/rfc4034#section-2.1.4)
|
||||
|
@ -1,5 +1,5 @@
|
||||
use super::*;
|
||||
|
||||
pub fn named_process() -> (NamedProcess, u16) {
|
||||
panic!("enable the desired tests with '--no-defaul-features --features=bind'")
|
||||
panic!("enable the desired tests with '--no-default-features --features=bind'")
|
||||
}
|
@ -135,12 +135,12 @@ fn test_create() {
|
||||
assert_eq!(result.response_code(), ResponseCode::YXRRSet);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_update() {
|
||||
// named_process();
|
||||
}
|
||||
// #[test]
|
||||
// fn test_update() {
|
||||
// // named_process();
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn test_delete() {
|
||||
// named_process();
|
||||
}
|
||||
// #[test]
|
||||
// fn test_delete() {
|
||||
// // named_process();
|
||||
// }
|
@ -49,9 +49,9 @@ path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
futures = "^0.1.6"
|
||||
rustls = "^0.5.8"
|
||||
rustls = "^0.8.0"
|
||||
tokio-core = "^0.1"
|
||||
tokio-rustls = "^0.1"
|
||||
tokio-rustls = "^0.2"
|
||||
# disables default features, i.e. openssl...
|
||||
trust-dns = { version = "^0.10", path = "../client", default-features = false }
|
||||
|
||||
|
@ -37,7 +37,7 @@ fn tls_new(certs: &[Certificate] /*, pkcs12: Option<Pkcs12>*/) -> io::Result<Arc
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if let Some(pkcs12) = pkcs12 {
|
||||
// try!(builder
|
||||
// .identity(pkcs12)
|
||||
|
@ -23,7 +23,7 @@ use trust_dns::error::*;
|
||||
use trust_dns::op::{Message, UpdateMessage, ResponseCode, Query};
|
||||
use trust_dns::rr::{DNSClass, Name, RData, Record, RecordType, RrKey, RecordSet};
|
||||
use trust_dns::rr::rdata::{NSEC, SIG};
|
||||
use trust_dns::rr::dnssec::{KeyPair, Signer, SupportedAlgorithms};
|
||||
use trust_dns::rr::dnssec::{hash, Signer, SupportedAlgorithms, Verifier};
|
||||
|
||||
use authority::{Journal, UpdateResult, ZoneType};
|
||||
use error::{PersistenceErrorKind, PersistenceResult};
|
||||
@ -502,20 +502,7 @@ impl Authority {
|
||||
None
|
||||
})
|
||||
.any(|key| {
|
||||
let pkey = KeyPair::from_public_bytes(key.public_key(), *key.algorithm());
|
||||
if let Err(error) = pkey {
|
||||
warn!("public key {:?} of {} could not be used: {}",
|
||||
key,
|
||||
name,
|
||||
error);
|
||||
return false;
|
||||
}
|
||||
|
||||
let pkey = pkey.unwrap();
|
||||
let signer: Signer = Signer::sig0(key.clone(), pkey, sig.signer_name().clone());
|
||||
|
||||
signer
|
||||
.verify_message(update_message, sig.sig(), sig)
|
||||
key.verify_message(update_message, sig.sig(), sig)
|
||||
.map(|_| {
|
||||
info!("verified sig: {:?} with key: {:?}", sig, key);
|
||||
true
|
||||
@ -1137,24 +1124,23 @@ impl Authority {
|
||||
for signer in self.secure_keys.iter() {
|
||||
let expiration = inception + signer.sig_duration();
|
||||
|
||||
let hash =
|
||||
signer.hash_rrset(rr_set.name(),
|
||||
self.class,
|
||||
rr_set.name().num_labels(),
|
||||
rr_set.record_type(),
|
||||
signer.algorithm(),
|
||||
rr_set.ttl(),
|
||||
expiration.timestamp() as u32,
|
||||
inception.timestamp() as u32,
|
||||
try!(signer.calculate_key_tag()),
|
||||
signer.signer_name(),
|
||||
// TODO: this is a nasty clone... the issue is that the vec
|
||||
// from records is of Vec<&R>, but we really want &[R]
|
||||
&rr_set
|
||||
.records(false, SupportedAlgorithms::new())
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect::<Vec<Record>>());
|
||||
let hash = hash::hash_rrset(rr_set.name(),
|
||||
self.class,
|
||||
rr_set.name().num_labels(),
|
||||
rr_set.record_type(),
|
||||
signer.algorithm(),
|
||||
rr_set.ttl(),
|
||||
expiration.timestamp() as u32,
|
||||
inception.timestamp() as u32,
|
||||
try!(signer.calculate_key_tag()),
|
||||
signer.signer_name(),
|
||||
// TODO: this is a nasty clone... the issue is that the vec
|
||||
// from records is of Vec<&R>, but we really want &[R]
|
||||
&rr_set
|
||||
.records(false, SupportedAlgorithms::new())
|
||||
.into_iter()
|
||||
.cloned()
|
||||
.collect::<Vec<Record>>());
|
||||
|
||||
// TODO, maybe chain these with some ETL operations instead?
|
||||
if hash.is_err() {
|
||||
|
@ -276,7 +276,7 @@ impl KeyConfig {
|
||||
Some("der") => Ok(KeyFormat::Der),
|
||||
Some("key") => Ok(KeyFormat::Pem), // TODO: deprecate this...
|
||||
Some("pem") => Ok(KeyFormat::Pem),
|
||||
Some("raw") => Ok(KeyFormat::Raw),
|
||||
Some("pk8") => Ok(KeyFormat::Pkcs8),
|
||||
e @ _ => {
|
||||
Err(ParseErrorKind::Msg(format!("extension not understood, '{:?}': {:?}",
|
||||
e,
|
||||
|
@ -253,6 +253,28 @@ fn load_key(zone_name: Name, key_config: &KeyConfig) -> Result<Signer, String> {
|
||||
.format()
|
||||
.map_err(|e| format!("bad key format: {}", e)));
|
||||
|
||||
// generate and write a new key if it does not exist
|
||||
if !key_path.exists() && key_config.create_if_absent() {
|
||||
info!("creating key: {:?}", key_path);
|
||||
|
||||
// TODO: establish proper ownership
|
||||
let mut file =
|
||||
try!(File::create(&key_path)
|
||||
.map_err(|e| format!("error creating private key file: {:?}: {}", key_path, e)));
|
||||
|
||||
let key_bytes: Vec<u8> =
|
||||
try!(format
|
||||
.generate_and_encode(algorithm, key_config.password())
|
||||
.map_err(|e| format!("could not generate key: {}", e)));
|
||||
|
||||
try!(file.write_all(&key_bytes)
|
||||
.or_else(|_| fs::remove_file(&key_path))
|
||||
.map_err(|e| {
|
||||
format!("error writing private key file: {:?}: {}", key_path, e)
|
||||
}));
|
||||
}
|
||||
|
||||
// read the key in
|
||||
let key: KeyPair = if key_path.exists() {
|
||||
info!("reading key: {:?}", key_path);
|
||||
|
||||
@ -267,28 +289,6 @@ fn load_key(zone_name: Name, key_config: &KeyConfig) -> Result<Signer, String> {
|
||||
try!(format
|
||||
.decode_key(&key_bytes, key_config.password(), algorithm)
|
||||
.map_err(|e| format!("could not decode key: {}", e)))
|
||||
} else if key_config.create_if_absent() {
|
||||
info!("creating key: {:?}", key_path);
|
||||
|
||||
// TODO: establish proper ownership
|
||||
let mut file =
|
||||
try!(File::create(&key_path)
|
||||
.map_err(|e| format!("error creating private key file: {:?}: {}", key_path, e)));
|
||||
|
||||
let key = try!(KeyPair::generate(algorithm)
|
||||
.map_err(|e| format!("could not generate key: {}", e)));
|
||||
let key_bytes: Vec<u8> =
|
||||
try!(format
|
||||
.encode_key(&key, key_config.password())
|
||||
.map_err(|e| format!("could not get key bytes: {}", e)));
|
||||
|
||||
try!(file.write_all(&key_bytes)
|
||||
.or_else(|_| fs::remove_file(&key_path))
|
||||
.map_err(|e| {
|
||||
format!("error writing private key file: {:?}: {}", key_path, e)
|
||||
}));
|
||||
|
||||
key
|
||||
} else {
|
||||
return Err(format!("file not found: {:?}", key_path));
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user