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:
Benjamin Fry 2017-05-17 12:31:32 -07:00 committed by GitHub
parent 6a0782693e
commit ea7d81804b
23 changed files with 1269 additions and 983 deletions

View File

@ -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

View File

@ -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
View File

@ -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"

View File

@ -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" }

View File

@ -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 }

View File

@ -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())
}

View File

@ -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,
}
}
}

View 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);
}
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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;

View 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"),
}
}
}

View File

@ -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());

View 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())
}
}

View File

@ -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)

View File

@ -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)

View File

@ -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'")
}

View File

@ -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();
// }

View File

@ -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 }

View File

@ -37,7 +37,7 @@ fn tls_new(certs: &[Certificate] /*, pkcs12: Option<Pkcs12>*/) -> io::Result<Arc
}));
}
}
// if let Some(pkcs12) = pkcs12 {
// try!(builder
// .identity(pkcs12)

View File

@ -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() {

View File

@ -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,

View File

@ -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));
};