so many lines of totally untested code
This commit is contained in:
parent
bba39afc6b
commit
641852481f
|
@ -49,7 +49,7 @@ version = "0.7.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
checksum = "fcb51a0695d8f838b1ee009b3fbf66bda078cd64590202a864a8f3e8c4315c47"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.8",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
@ -154,6 +154,12 @@ version = "0.21.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64ct"
|
||||||
|
version = "1.5.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b645a089122eccb6111b4f81cbc1a49f5900ac4666bb93ac027feaecf15607bf"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "binascii"
|
name = "binascii"
|
||||||
version = "0.1.4"
|
version = "0.1.4"
|
||||||
|
@ -166,6 +172,15 @@ version = "1.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "block-buffer"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "block-buffer"
|
name = "block-buffer"
|
||||||
version = "0.10.3"
|
version = "0.10.3"
|
||||||
|
@ -252,6 +267,12 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "const-oid"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cec318a675afcb6a1ea1d4340e2d377e56e47c266f28043ceccbf4412ddfdd3b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "constant_time_eq"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
|
@ -270,7 +291,7 @@ dependencies = [
|
||||||
"hmac",
|
"hmac",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand",
|
"rand",
|
||||||
"sha2",
|
"sha2 0.10.6",
|
||||||
"subtle",
|
"subtle",
|
||||||
"time 0.3.17",
|
"time 0.3.17",
|
||||||
"version_check",
|
"version_check",
|
||||||
|
@ -351,7 +372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"generic-array",
|
"generic-array",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
"typenum",
|
"typenum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -364,6 +385,34 @@ dependencies = [
|
||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek"
|
||||||
|
version = "3.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b9fdf9972b2bd6af2d913799d9ebc165ea4d2e65878e329d9c6b372c4491b61"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"digest 0.9.0",
|
||||||
|
"rand_core 0.5.1",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "curve25519-dalek"
|
||||||
|
version = "4.0.0-rc.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8d4ba9852b42210c7538b75484f9daa0655e9a3ac04f693747bb0f02cf3cfe16"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"digest 0.10.6",
|
||||||
|
"fiat-crypto",
|
||||||
|
"packed_simd_2",
|
||||||
|
"platforms",
|
||||||
|
"subtle",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cxx"
|
name = "cxx"
|
||||||
version = "1.0.91"
|
version = "1.0.91"
|
||||||
|
@ -408,6 +457,16 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "der"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f1a467a65c5e759bce6e65eaf91cc29f466cdc57cb65777bd646872a8a1fd4de"
|
||||||
|
dependencies = [
|
||||||
|
"const-oid",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "devise"
|
name = "devise"
|
||||||
version = "0.3.1"
|
version = "0.3.1"
|
||||||
|
@ -441,13 +500,22 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"block-buffer",
|
"block-buffer 0.10.3",
|
||||||
"crypto-common",
|
"crypto-common",
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
@ -478,6 +546,29 @@ version = "0.15.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0"
|
checksum = "03d8c417d7a8cb362e0c37e5d815f5eb7c37f79ff93707329d5a194e42e54ca0"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519"
|
||||||
|
version = "2.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cf420a7ec85d98495b0c34aa4a58ca117f982ffbece111aeb545160148d7010"
|
||||||
|
dependencies = [
|
||||||
|
"pkcs8",
|
||||||
|
"signature",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ed25519-dalek"
|
||||||
|
version = "2.0.0-pre.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7bd577ba9d4bcab443cac60003d8fd32c638e7024a3ec92c200d7af5d2c397ed"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek 4.0.0-rc.1",
|
||||||
|
"ed25519",
|
||||||
|
"serde",
|
||||||
|
"sha2 0.10.6",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.8.1"
|
version = "1.8.1"
|
||||||
|
@ -508,6 +599,12 @@ dependencies = [
|
||||||
"instant",
|
"instant",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fiat-crypto"
|
||||||
|
version = "0.1.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a214f5bb88731d436478f3ae1f8a277b62124089ba9fb67f4f93fb100ef73c90"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "figment"
|
name = "figment"
|
||||||
version = "0.10.8"
|
version = "0.10.8"
|
||||||
|
@ -661,6 +758,17 @@ dependencies = [
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "getrandom"
|
||||||
|
version = "0.1.16"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.8"
|
version = "0.2.8"
|
||||||
|
@ -773,7 +881,7 @@ version = "0.12.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest",
|
"digest 0.10.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -917,6 +1025,12 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ipnet"
|
||||||
|
version = "2.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "30e22bd8629359895450b59ea7a776c850561b96a3b1d31321c1949d9e6c9146"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.10.5"
|
version = "0.10.5"
|
||||||
|
@ -953,6 +1067,12 @@ version = "0.2.139"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libm"
|
||||||
|
version = "0.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7fc7aa29613bd6a620df431842069224d8bc9011086b1db4c0e0cd47fa03ec9a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "link-cplusplus"
|
name = "link-cplusplus"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
|
@ -1011,7 +1131,7 @@ version = "0.10.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca"
|
checksum = "6365506850d44bff6e2fbcb5176cf63650e48bd45ef2fe2665ae1570e0f4b9ca"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"digest",
|
"digest 0.10.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1223,6 +1343,16 @@ version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "packed_simd_2"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1914cd452d8fccd6f9db48147b29fd4ae05bea9dc5d9ad578509f72415de282"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"libm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "parking_lot"
|
name = "parking_lot"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
|
@ -1300,6 +1430,15 @@ dependencies = [
|
||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pem"
|
||||||
|
version = "1.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.13.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.2.0"
|
version = "2.2.0"
|
||||||
|
@ -1318,12 +1457,28 @@ version = "0.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pkcs8"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9eca2c590a5f85da82668fa685c09ce2888b9430e83299debf1f34b65fd4a4ba"
|
||||||
|
dependencies = [
|
||||||
|
"der",
|
||||||
|
"spki",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pkg-config"
|
name = "pkg-config"
|
||||||
version = "0.3.26"
|
version = "0.3.26"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "platforms"
|
||||||
|
version = "3.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e3d7ddaed09e0eb771a79ab0fd64609ba0afb0a8366421957936ad14cbd13630"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "png"
|
name = "png"
|
||||||
version = "0.17.7"
|
version = "0.17.7"
|
||||||
|
@ -1382,6 +1537,15 @@ version = "1.8.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142"
|
checksum = "4339fc7a1021c9c1621d87f5e3505f2805c8c105420ba2f2a4df86814590c142"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quick-protobuf"
|
||||||
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9d6da84cc204722a989e01ba2f6e1e276e190f22263d0cb6ce8526fcdb0d2e1f"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.23"
|
version = "1.0.23"
|
||||||
|
@ -1399,7 +1563,7 @@ checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"rand_chacha",
|
"rand_chacha",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1409,7 +1573,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ppv-lite86",
|
"ppv-lite86",
|
||||||
"rand_core",
|
"rand_core 0.6.4",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rand_core"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.1.16",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1418,7 +1591,7 @@ version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1436,7 +1609,7 @@ version = "0.4.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.8",
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
]
|
]
|
||||||
|
@ -1686,7 +1859,20 @@ checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"digest",
|
"digest 0.10.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha2"
|
||||||
|
version = "0.9.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4d58a1e1bf39749807d89cf2d98ac2dfa0ff1cb3faa38fbb64dd88ac8013d800"
|
||||||
|
dependencies = [
|
||||||
|
"block-buffer 0.9.0",
|
||||||
|
"cfg-if",
|
||||||
|
"cpufeatures",
|
||||||
|
"digest 0.9.0",
|
||||||
|
"opaque-debug",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1697,7 +1883,17 @@ checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
"cpufeatures",
|
"cpufeatures",
|
||||||
"digest",
|
"digest 0.10.6",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sha256"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "328169f167261957e83d82be47f9e36629e257c62308129033d7f7e7c173d180"
|
||||||
|
dependencies = [
|
||||||
|
"hex",
|
||||||
|
"sha2 0.9.9",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1718,6 +1914,12 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signature"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8fe458c98333f9c8152221191a77e2a44e8325d0193484af2e9421a53019e57d"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "slab"
|
name = "slab"
|
||||||
version = "0.4.7"
|
version = "0.4.7"
|
||||||
|
@ -1749,6 +1951,16 @@ version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
|
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "spki"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67cf02bbac7a337dc36e4f5a693db6c21e7863f45070f7064577eb4367a3212b"
|
||||||
|
dependencies = [
|
||||||
|
"base64ct",
|
||||||
|
"der",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sqlformat"
|
name = "sqlformat"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -1809,7 +2021,7 @@ dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2",
|
"sha2 0.10.6",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
"sqlformat",
|
"sqlformat",
|
||||||
"sqlx-rt",
|
"sqlx-rt",
|
||||||
|
@ -1832,7 +2044,7 @@ dependencies = [
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"sha2",
|
"sha2 0.10.6",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-rt",
|
"sqlx-rt",
|
||||||
"syn",
|
"syn",
|
||||||
|
@ -1896,6 +2108,18 @@ dependencies = [
|
||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "synstructure"
|
||||||
|
version = "0.12.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"unicode-xid",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tempfile"
|
name = "tempfile"
|
||||||
version = "3.3.0"
|
version = "3.3.0"
|
||||||
|
@ -2128,7 +2352,7 @@ dependencies = [
|
||||||
"qrcodegen",
|
"qrcodegen",
|
||||||
"rand",
|
"rand",
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2",
|
"sha2 0.10.6",
|
||||||
"url",
|
"url",
|
||||||
"urlencoding",
|
"urlencoding",
|
||||||
]
|
]
|
||||||
|
@ -2227,6 +2451,15 @@ dependencies = [
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trifid-pki"
|
name = "trifid-pki"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"ed25519-dalek",
|
||||||
|
"hex",
|
||||||
|
"ipnet",
|
||||||
|
"pem",
|
||||||
|
"quick-protobuf",
|
||||||
|
"sha256",
|
||||||
|
"x25519-dalek",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "try-lock"
|
name = "try-lock"
|
||||||
|
@ -2338,7 +2571,7 @@ version = "1.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
|
checksum = "1674845326ee10d37ca60470760d4288a6f80f304007d92e5c53bab78c9cfd79"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom 0.2.8",
|
||||||
"rand",
|
"rand",
|
||||||
"uuid-macro-internal",
|
"uuid-macro-internal",
|
||||||
]
|
]
|
||||||
|
@ -2382,6 +2615,12 @@ dependencies = [
|
||||||
"try-lock",
|
"try-lock",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wasi"
|
||||||
|
version = "0.9.0+wasi-snapshot-preview1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "wasi"
|
||||||
version = "0.10.0+wasi-snapshot-preview1"
|
version = "0.10.0+wasi-snapshot-preview1"
|
||||||
|
@ -2623,8 +2862,40 @@ version = "0.42.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x25519-dalek"
|
||||||
|
version = "2.0.0-pre.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e5da623d8af10a62342bcbbb230e33e58a63255a58012f8653c578e54bab48df"
|
||||||
|
dependencies = [
|
||||||
|
"curve25519-dalek 3.2.0",
|
||||||
|
"rand_core 0.6.4",
|
||||||
|
"zeroize",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "yansi"
|
name = "yansi"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize"
|
||||||
|
version = "1.5.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c394b5bd0c6f669e7275d9c20aa90ae064cb22e75a1cad54e1b34088034b149f"
|
||||||
|
dependencies = [
|
||||||
|
"zeroize_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "zeroize_derive"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44bf07cb3e50ea2003396695d58bf46bc9887a1f362260446fad6bc4e79bd36c"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
"synstructure",
|
||||||
|
]
|
||||||
|
|
|
@ -7,3 +7,10 @@ description = "A rust implementation of the Nebula PKI system"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
pem = "1.1.1"
|
||||||
|
x25519-dalek = "2.0.0-pre.1"
|
||||||
|
ed25519-dalek = "2.0.0-pre.0"
|
||||||
|
ipnet = "2.7.1"
|
||||||
|
quick-protobuf = "0.8.1"
|
||||||
|
hex = "0.4.3"
|
||||||
|
sha256 = "1.1.2"
|
|
@ -0,0 +1,101 @@
|
||||||
|
//! Structs to represent a pool of CA's and blacklisted certificates
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use ed25519_dalek::VerifyingKey;
|
||||||
|
use crate::cert::{deserialize_nebula_certificate_from_pem, NebulaCertificate};
|
||||||
|
|
||||||
|
/// A pool of trusted CA certificates, and certificates that should be blocked.
|
||||||
|
/// This is equivalent to the `pki` section in a typical Nebula config.yml.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct NebulaCAPool {
|
||||||
|
/// The list of CA root certificates that should be trusted.
|
||||||
|
pub cas: HashMap<String, NebulaCertificate>,
|
||||||
|
/// The list of blocklisted certificate fingerprints
|
||||||
|
pub cert_blocklist: Vec<String>,
|
||||||
|
/// True if any of the member CAs certificates are expired. Must be handled.
|
||||||
|
pub expired: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NebulaCAPool {
|
||||||
|
/// Create a new, blank CA pool
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new CA pool from a set of PEM encoded CA certificates.
|
||||||
|
/// If any of the certificates are expired, the pool will **still be returned**, with the expired flag set.
|
||||||
|
/// This must be handled properly.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if PEM data provided was invalid.
|
||||||
|
pub fn new_from_pem(bytes: &[u8]) -> Result<Self, Box<dyn Error>> {
|
||||||
|
let pems = pem::parse_many(bytes)?;
|
||||||
|
|
||||||
|
let mut pool = Self::new();
|
||||||
|
|
||||||
|
for cert in pems {
|
||||||
|
match pool.add_ca_certificate(pem::encode(&cert).as_bytes()) {
|
||||||
|
Ok(did_expire) => if did_expire { pool.expired = true },
|
||||||
|
Err(e) => return Err(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(pool)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a given CA certificate to the CA pool. If the certificate is expired, it will **still be added** - the return value will be `true` instead of `false`
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the certificate is invalid in any way.
|
||||||
|
pub fn add_ca_certificate(&mut self, bytes: &[u8]) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let cert = deserialize_nebula_certificate_from_pem(bytes)?;
|
||||||
|
|
||||||
|
if !cert.details.is_ca {
|
||||||
|
return Err(CaPoolError::NotACA.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
if !cert.check_signature(&VerifyingKey::from_bytes(&cert.details.public_key)?)? {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Checks if the given certificate is blocklisted
|
||||||
|
pub fn is_blocklisted(&self, cert: &NebulaCertificate) -> bool {
|
||||||
|
let Ok(h) = cert.sha256sum() else { return false };
|
||||||
|
self.cert_blocklist.contains(&h)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the CA certificate used to sign the given certificate
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the certificate does not have an issuer attached (it is self-signed)
|
||||||
|
pub fn get_ca_for_cert(&self, cert: &NebulaCertificate) -> Result<Option<&NebulaCertificate>, Box<dyn Error>> {
|
||||||
|
if cert.details.issuer == String::new() {
|
||||||
|
return Err(CaPoolError::NoIssuer.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self.cas.get(&cert.details.issuer))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
/// A list of errors that can happen when working with a CA Pool
|
||||||
|
pub enum CaPoolError {
|
||||||
|
/// Tried to add a non-CA cert to the CA pool
|
||||||
|
NotACA,
|
||||||
|
/// Tried to add a non-self-signed cert to the CA pool (all CAs must be root certificates)
|
||||||
|
NotSelfSigned,
|
||||||
|
/// Tried to look up a certificate that does not have an issuer field
|
||||||
|
NoIssuer
|
||||||
|
}
|
||||||
|
impl Error for CaPoolError {}
|
||||||
|
impl Display for CaPoolError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::NotACA => write!(f, "Tried to add a non-CA cert to the CA pool"),
|
||||||
|
Self::NotSelfSigned => write!(f, "Tried to add a non-self-signed cert to the CA pool (all CAs must be root certificates)"),
|
||||||
|
Self::NoIssuer => write!(f, "Tried to look up a certificate with a null issuer field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,505 @@
|
||||||
|
//! Manage Nebula PKI Certificates
|
||||||
|
//! This is pretty much a direct port of nebula/cert/cert.go
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::ops::Add;
|
||||||
|
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||||
|
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
|
||||||
|
use ed25519_dalek::ed25519::pkcs8::spki::der::Encode;
|
||||||
|
use ipnet::{Ipv4Net};
|
||||||
|
use pem::Pem;
|
||||||
|
use quick_protobuf::{BytesReader, MessageRead, Writer};
|
||||||
|
use sha256::digest;
|
||||||
|
use x25519_dalek::{PublicKey, StaticSecret};
|
||||||
|
use crate::ca::NebulaCAPool;
|
||||||
|
use crate::cert_codec::{RawNebulaCertificate, RawNebulaCertificateDetails};
|
||||||
|
|
||||||
|
/// The length, in bytes, of public keys
|
||||||
|
pub const PUBLIC_KEY_LENGTH: i32 = 32;
|
||||||
|
|
||||||
|
/// The PEM banner for certificates
|
||||||
|
pub const CERT_BANNER: &str = "NEBULA CERTIFICATE";
|
||||||
|
/// The PEM banner for X25519 private keys
|
||||||
|
pub const X25519_PRIVATE_KEY_BANNER: &str = "NEBULA X25519 PRIVATE KEY";
|
||||||
|
/// The PEM banner for X25519 public keys
|
||||||
|
pub const X25519_PUBLIC_KEY_BANNER: &str = "NEBULA X25519 PUBLIC KEY";
|
||||||
|
/// The PEM banner for Ed25519 private keys
|
||||||
|
pub const ED25519_PRIVATE_KEY_BANNER: &str = "NEBULA ED25519 PRIVATE KEY";
|
||||||
|
/// The PEM banner for Ed25519 public keys
|
||||||
|
pub const ED25519_PUBLIC_KEY_BANNER: &str = "NEBULA ED25519 PUBLIC KEY";
|
||||||
|
|
||||||
|
/// A Nebula PKI certificate
|
||||||
|
pub struct NebulaCertificate {
|
||||||
|
/// The signed data of this certificate
|
||||||
|
pub details: NebulaCertificateDetails,
|
||||||
|
/// The Ed25519 signature of this certificate
|
||||||
|
pub signature: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The signed details contained in a Nebula PKI certificate
|
||||||
|
pub struct NebulaCertificateDetails {
|
||||||
|
/// The name of the identity this certificate was issued for
|
||||||
|
pub name: String,
|
||||||
|
/// The IPv4 addresses issued to this node
|
||||||
|
pub ips: Vec<Ipv4Net>,
|
||||||
|
/// The IPv4 subnets this node is responsible for routing
|
||||||
|
pub subnets: Vec<Ipv4Net>,
|
||||||
|
/// The groups this node is a part of
|
||||||
|
pub groups: Vec<String>,
|
||||||
|
/// Certificate start date and time
|
||||||
|
pub not_before: SystemTime,
|
||||||
|
/// Certificate expiry date and time
|
||||||
|
pub not_after: SystemTime,
|
||||||
|
/// Public key
|
||||||
|
pub public_key: [u8; PUBLIC_KEY_LENGTH as usize],
|
||||||
|
/// Is this node a CA?
|
||||||
|
pub is_ca: bool,
|
||||||
|
/// SHA256 of issuer certificate. If blank, this cert is self-signed.
|
||||||
|
pub issuer: String
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of errors that can occur parsing certificates
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum CertificateError {
|
||||||
|
/// Attempted to deserialize a certificate from an empty byte array
|
||||||
|
EmptyByteArray,
|
||||||
|
/// The encoded Details field is null
|
||||||
|
NilDetails,
|
||||||
|
/// The encoded Ips field is not formatted correctly
|
||||||
|
IpsNotPairs,
|
||||||
|
/// The encoded Subnets field is not formatted correctly
|
||||||
|
SubnetsNotPairs,
|
||||||
|
/// Signatures are expected to be 64 bytes but the signature on the certificate was a different length
|
||||||
|
WrongSigLength,
|
||||||
|
/// Public keys are expected to be 32 bytes but the public key on this cert is not
|
||||||
|
WrongKeyLength,
|
||||||
|
/// Certificates should have the PEM tag `NEBULA CERTIFICATE`, but this block did not
|
||||||
|
WrongPemTag,
|
||||||
|
/// This certificate either is not yet valid or has already expired
|
||||||
|
Expired,
|
||||||
|
/// The public key does not match the expected value
|
||||||
|
KeyMismatch
|
||||||
|
}
|
||||||
|
impl Display for CertificateError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::EmptyByteArray => write!(f, "Certificate bytearray is empty"),
|
||||||
|
Self::NilDetails => write!(f, "The encoded Details field is null"),
|
||||||
|
Self::IpsNotPairs => write!(f, "encoded IPs should be in pairs, an odd number was found"),
|
||||||
|
Self::SubnetsNotPairs => write!(f, "encoded subnets should be in pairs, an odd number was found"),
|
||||||
|
Self::WrongSigLength => write!(f, "Signature should be 64 bytes but is a different size"),
|
||||||
|
Self::WrongKeyLength => write!(f, "Public keys are expected to be 32 bytes but the public key on this cert is not"),
|
||||||
|
Self::WrongPemTag => write!(f, "Certificates should have the PEM tag `NEBULA CERTIFICATE`, but this block did not"),
|
||||||
|
Self::Expired => write!(f, "This certificate either is not yet valid or has already expired"),
|
||||||
|
Self::KeyMismatch => write!(f, "Key does not match expected value")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Error for CertificateError {}
|
||||||
|
|
||||||
|
fn map_cidr_pairs(pairs: &[u32]) -> Result<Vec<Ipv4Net>, Box<dyn Error>> {
|
||||||
|
let mut res_vec = vec![];
|
||||||
|
for pair in pairs.chunks(2) {
|
||||||
|
res_vec.push(Ipv4Net::with_netmask(Ipv4Addr::from(pair[0]), Ipv4Addr::from(pair[1]))?);
|
||||||
|
}
|
||||||
|
Ok(res_vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a protobuf-encoded certificate bytearray, deserialize it into a `NebulaCertificate` object.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if there is a protobuf parsing error, or if the certificate data is invalid.
|
||||||
|
/// # Panics
|
||||||
|
pub fn deserialize_nebula_certificate(bytes: &[u8]) -> Result<NebulaCertificate, Box<dyn Error>> {
|
||||||
|
if bytes.is_empty() {
|
||||||
|
return Err(CertificateError::EmptyByteArray.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut reader = BytesReader::from_bytes(bytes);
|
||||||
|
|
||||||
|
let raw_cert = RawNebulaCertificate::from_reader(&mut reader, bytes)?;
|
||||||
|
|
||||||
|
let details = raw_cert.Details.ok_or(CertificateError::NilDetails)?;
|
||||||
|
|
||||||
|
if details.Ips.len() % 2 != 0 {
|
||||||
|
return Err(CertificateError::IpsNotPairs.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
if details.Subnets.len() % 2 != 0 {
|
||||||
|
return Err(CertificateError::SubnetsNotPairs.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut nebula_cert;
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
{
|
||||||
|
nebula_cert = NebulaCertificate {
|
||||||
|
details: NebulaCertificateDetails {
|
||||||
|
name: details.Name.to_string(),
|
||||||
|
ips: map_cidr_pairs(&details.Ips)?,
|
||||||
|
subnets: map_cidr_pairs(&details.Subnets)?,
|
||||||
|
groups: details.Groups.iter().map(std::string::ToString::to_string).collect(),
|
||||||
|
not_before: SystemTime::UNIX_EPOCH.add(Duration::from_secs(details.NotBefore as u64)),
|
||||||
|
not_after: SystemTime::UNIX_EPOCH.add(Duration::from_secs(details.NotAfter as u64)),
|
||||||
|
public_key: [0u8; 32],
|
||||||
|
is_ca: details.IsCA,
|
||||||
|
issuer: hex::encode(details.Issuer),
|
||||||
|
},
|
||||||
|
signature: vec![],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
nebula_cert.signature = raw_cert.Signature;
|
||||||
|
|
||||||
|
if details.PublicKey.len() != 32 {
|
||||||
|
return Err(CertificateError::WrongKeyLength.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unwrap_used)] { nebula_cert.details.public_key = details.PublicKey.try_into().unwrap(); }
|
||||||
|
|
||||||
|
Ok(nebula_cert)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of errors that can occur parsing keys
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum KeyError {
|
||||||
|
/// Keys should have their associated PEM tags but this had the wrong one
|
||||||
|
WrongPemTag
|
||||||
|
}
|
||||||
|
impl Display for KeyError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::WrongPemTag => write!(f, "Keys should have their associated PEM tags but this had the wrong one")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Error for KeyError {}
|
||||||
|
|
||||||
|
|
||||||
|
/// Deserialize the first PEM block in the given byte array into a `NebulaCertificate`
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the PEM data is invalid, or if there is an error parsing the certificate (see `deserialize_nebula_certificate`)
|
||||||
|
pub fn deserialize_nebula_certificate_from_pem(bytes: &[u8]) -> Result<NebulaCertificate, Box<dyn Error>> {
|
||||||
|
let pem = pem::parse(bytes)?;
|
||||||
|
if pem.tag != CERT_BANNER {
|
||||||
|
return Err(CertificateError::WrongPemTag.into())
|
||||||
|
}
|
||||||
|
deserialize_nebula_certificate(&pem.contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple helper to PEM encode an X25519 private key
|
||||||
|
pub fn serialize_x25519_private(bytes: &[u8]) -> Vec<u8> {
|
||||||
|
pem::encode(&Pem {
|
||||||
|
tag: X25519_PRIVATE_KEY_BANNER.to_string(),
|
||||||
|
contents: bytes.to_vec(),
|
||||||
|
}).as_bytes().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple helper to PEM encode an X25519 public key
|
||||||
|
pub fn serialize_x25519_public(bytes: &[u8]) -> Vec<u8> {
|
||||||
|
pem::encode(&Pem {
|
||||||
|
tag: X25519_PUBLIC_KEY_BANNER.to_string(),
|
||||||
|
contents: bytes.to_vec(),
|
||||||
|
}).as_bytes().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to deserialize a PEM encoded X25519 private key
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the PEM data is invalid or has the wrong tag
|
||||||
|
pub fn deserialize_x25519_private(bytes: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
let pem = pem::parse(bytes)?;
|
||||||
|
if pem.tag != X25519_PRIVATE_KEY_BANNER {
|
||||||
|
return Err(KeyError::WrongPemTag.into())
|
||||||
|
}
|
||||||
|
Ok(pem.contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to deserialize a PEM encoded X25519 public key
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the PEM data is invalid or has the wrong tag
|
||||||
|
pub fn deserialize_x25519_public(bytes: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
let pem = pem::parse(bytes)?;
|
||||||
|
if pem.tag != X25519_PUBLIC_KEY_BANNER {
|
||||||
|
return Err(KeyError::WrongPemTag.into())
|
||||||
|
}
|
||||||
|
Ok(pem.contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple helper to PEM encode an Ed25519 private key
|
||||||
|
pub fn serialize_ed25519_private(bytes: &[u8]) -> Vec<u8> {
|
||||||
|
pem::encode(&Pem {
|
||||||
|
tag: ED25519_PRIVATE_KEY_BANNER.to_string(),
|
||||||
|
contents: bytes.to_vec(),
|
||||||
|
}).as_bytes().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Simple helper to PEM encode an Ed25519 public key
|
||||||
|
pub fn serialize_ed25519_public(bytes: &[u8]) -> Vec<u8> {
|
||||||
|
pem::encode(&Pem {
|
||||||
|
tag: ED25519_PUBLIC_KEY_BANNER.to_string(),
|
||||||
|
contents: bytes.to_vec(),
|
||||||
|
}).as_bytes().to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to deserialize a PEM encoded Ed25519 private key
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the PEM data is invalid or has the wrong tag
|
||||||
|
pub fn deserialize_ed25519_private(bytes: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
let pem = pem::parse(bytes)?;
|
||||||
|
if pem.tag != ED25519_PRIVATE_KEY_BANNER {
|
||||||
|
return Err(KeyError::WrongPemTag.into())
|
||||||
|
}
|
||||||
|
Ok(pem.contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempt to deserialize a PEM encoded Ed25519 public key
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the PEM data is invalid or has the wrong tag
|
||||||
|
pub fn deserialize_ed25519_public(bytes: &[u8]) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
let pem = pem::parse(bytes)?;
|
||||||
|
if pem.tag != ED25519_PUBLIC_KEY_BANNER {
|
||||||
|
return Err(KeyError::WrongPemTag.into())
|
||||||
|
}
|
||||||
|
Ok(pem.contents)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NebulaCertificate {
|
||||||
|
/// Sign a nebula certificate with the provided private key
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the certificate could not be serialized or signed.
|
||||||
|
pub fn sign(&mut self, key: &SigningKey) -> Result<(), Box<dyn Error>> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
let mut writer = Writer::new(&mut out);
|
||||||
|
writer.write_message(&self.get_raw_details())?;
|
||||||
|
|
||||||
|
let sig = key.sign(&out).to_bytes();
|
||||||
|
|
||||||
|
self.signature = match sig.to_vec() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(_) => return Err("signature error".into())
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify the signature on a certificate with the provided public key
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the certificate could not be serialized or the signature could not be checked.
|
||||||
|
pub fn check_signature(&self, key: &VerifyingKey) -> Result<bool, Box<dyn Error>> {
|
||||||
|
let mut out = Vec::new();
|
||||||
|
let mut writer = Writer::new(&mut out);
|
||||||
|
writer.write_message(&self.get_raw_details())?;
|
||||||
|
|
||||||
|
Ok(key.verify(&out, &Signature::try_from(&*self.signature)?).is_ok())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the signature is too young or too old compared to the provided time
|
||||||
|
pub fn expired(&self, time: SystemTime) -> bool {
|
||||||
|
self.details.not_before > time || self.details.not_after < time
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verify will ensure a certificate is good in all respects (expiry, group membership, signature, cert blocklist, etc)
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if there is an error parsing the cert or the CA pool.
|
||||||
|
pub fn verify(&self, time: SystemTime, ca_pool: &NebulaCAPool) -> Result<CertificateValidity, Box<dyn Error>> {
|
||||||
|
if ca_pool.is_blocklisted(self) {
|
||||||
|
return Ok(CertificateValidity::Blocklisted);
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(signer) = ca_pool.get_ca_for_cert(self)? else { return Ok(CertificateValidity::NotSignedByThisCAPool) };
|
||||||
|
|
||||||
|
if signer.expired(time) {
|
||||||
|
return Ok(CertificateValidity::RootCertExpired)
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.expired(time) {
|
||||||
|
return Ok(CertificateValidity::CertExpired)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.check_signature(&VerifyingKey::from_bytes(&signer.details.public_key)?)? {
|
||||||
|
return Ok(CertificateValidity::BadSignature)
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(self.check_root_constraints(signer))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make sure that this certificate does not break any of the constraints set by the signing certificate
|
||||||
|
pub fn check_root_constraints(&self, signer: &Self) -> CertificateValidity {
|
||||||
|
// Make sure this cert doesn't expire after the signer
|
||||||
|
if signer.details.not_before < self.details.not_before {
|
||||||
|
return CertificateValidity::CertExpiresAfterSigner;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure this cert doesn't come into validity before the root
|
||||||
|
if signer.details.not_before > self.details.not_before {
|
||||||
|
return CertificateValidity::CertValidBeforeSigner;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the signer contains a limited set of groups, make sure this cert only has a subset of them
|
||||||
|
if !signer.details.groups.is_empty() {
|
||||||
|
for group in &self.details.groups {
|
||||||
|
if !signer.details.groups.contains(group) {
|
||||||
|
return CertificateValidity::GroupNotPresentOnSigner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the signer contains a limited set of IP ranges, make sure the cert only contains a subset
|
||||||
|
if !signer.details.ips.is_empty() {
|
||||||
|
for ip in &self.details.ips {
|
||||||
|
if !net_match(*ip, &signer.details.ips) {
|
||||||
|
return CertificateValidity::IPNotPresentOnSigner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the signer contains a limited set of subnets, make sure the cert only contains a subset
|
||||||
|
if !signer.details.subnets.is_empty() {
|
||||||
|
for subnet in &self.details.subnets {
|
||||||
|
if !net_match(*subnet, &signer.details.subnets) {
|
||||||
|
return CertificateValidity::SubnetNotPresentOnSigner;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CertificateValidity::Ok
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::unwrap_used)]
|
||||||
|
/// Verify if the given private key corresponds to the public key used to sign this certificate
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if either keys are invalid.
|
||||||
|
/// # Panics
|
||||||
|
/// This function, while containing calls to unwrap, has proper bounds checking and will not panic.
|
||||||
|
pub fn verify_private_key(&self, key: &[u8]) -> Result<(), Box<dyn Error>> {
|
||||||
|
if self.details.is_ca {
|
||||||
|
// convert the keys
|
||||||
|
if key.len() != 64 {
|
||||||
|
return Err("key not 64-bytes long".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let actual_private_key: [u8; 32] = (&key[..32]).try_into().unwrap();
|
||||||
|
|
||||||
|
if PublicKey::from(&StaticSecret::from(actual_private_key)) != PublicKey::from(self.details.public_key) {
|
||||||
|
return Err(CertificateError::KeyMismatch.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
if key.len() != 32 {
|
||||||
|
return Err("key not 32-bytes long".into())
|
||||||
|
}
|
||||||
|
|
||||||
|
let pubkey = x25519_dalek::x25519(key.try_into().unwrap(), x25519_dalek::X25519_BASEPOINT_BYTES);
|
||||||
|
if pubkey != self.details.public_key {
|
||||||
|
return Err(CertificateError::KeyMismatch.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Get a protobuf-ready raw struct, ready for serialization
|
||||||
|
#[allow(clippy::expect_used)]
|
||||||
|
#[allow(clippy::cast_possible_wrap)]
|
||||||
|
/// # Panics
|
||||||
|
/// This function will panic if time went backwards, or if the certificate contains extremely invalid data.
|
||||||
|
pub fn get_raw_details(&self) -> RawNebulaCertificateDetails {
|
||||||
|
|
||||||
|
let mut raw = RawNebulaCertificateDetails {
|
||||||
|
Name: self.details.name.clone(),
|
||||||
|
Ips: vec![],
|
||||||
|
Subnets: vec![],
|
||||||
|
Groups: self.details.groups.iter().map(std::convert::Into::into).collect(),
|
||||||
|
NotBefore: self.details.not_before.duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() as i64,
|
||||||
|
NotAfter: self.details.not_after.duration_since(UNIX_EPOCH).expect("Time went backwards").as_secs() as i64,
|
||||||
|
PublicKey: self.details.public_key.into(),
|
||||||
|
IsCA: self.details.is_ca,
|
||||||
|
Issuer: hex::decode(&self.details.issuer).expect("Issuer was not a hex-encoded value"),
|
||||||
|
};
|
||||||
|
|
||||||
|
for ip_net in &self.details.ips {
|
||||||
|
raw.Ips.push(ip_net.addr().into());
|
||||||
|
raw.Ips.push(ip_net.netmask().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
for subnet in &self.details.subnets {
|
||||||
|
raw.Subnets.push(subnet.addr().into());
|
||||||
|
raw.Subnets.push(subnet.netmask().into());
|
||||||
|
}
|
||||||
|
|
||||||
|
raw
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Will serialize this cert into a protobuf byte array.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if protobuf was unable to serialize the data.
|
||||||
|
pub fn serialize(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
let raw_cert = RawNebulaCertificate {
|
||||||
|
Details: Some(self.get_raw_details()),
|
||||||
|
Signature: self.signature.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut out = vec![];
|
||||||
|
let mut writer = Writer::new(&mut out);
|
||||||
|
writer.write_message(&raw_cert)?;
|
||||||
|
|
||||||
|
Ok(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Will serialize this cert into a PEM byte array.
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if protobuf was unable to serialize the data.
|
||||||
|
pub fn serialize_to_pem(&self) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
let pbuf_bytes = self.serialize()?;
|
||||||
|
|
||||||
|
Ok(pem::encode(&Pem {
|
||||||
|
tag: CERT_BANNER.to_string(),
|
||||||
|
contents: pbuf_bytes,
|
||||||
|
}).as_bytes().to_vec())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the fingerprint of this certificate
|
||||||
|
/// # Errors
|
||||||
|
/// This functiom will return an error if protobuf was unable to serialize the cert.
|
||||||
|
pub fn sha256sum(&self) -> Result<String, Box<dyn Error>> {
|
||||||
|
let pbuf_bytes = self.serialize()?;
|
||||||
|
|
||||||
|
Ok(digest(&pbuf_bytes[..]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A list of possible errors that can happen validating a certificate
|
||||||
|
pub enum CertificateValidity {
|
||||||
|
/// There are no issues with this certificate
|
||||||
|
Ok,
|
||||||
|
/// This cert has been blocklisted in the given CA pool
|
||||||
|
Blocklisted,
|
||||||
|
/// The certificate that signed this cert is expired
|
||||||
|
RootCertExpired,
|
||||||
|
/// This cert is expired
|
||||||
|
CertExpired,
|
||||||
|
/// This cert's signature is invalid
|
||||||
|
BadSignature,
|
||||||
|
/// This cert was not signed by any CAs in the CA pool
|
||||||
|
NotSignedByThisCAPool,
|
||||||
|
/// This cert expires after the signer's cert expires
|
||||||
|
CertExpiresAfterSigner,
|
||||||
|
/// This cert enters validity before the signer's cert does
|
||||||
|
CertValidBeforeSigner,
|
||||||
|
/// A group present on this certificate is not present on the signer's certificate
|
||||||
|
GroupNotPresentOnSigner,
|
||||||
|
/// An IP present on this certificate is not present on the signer's certificate
|
||||||
|
IPNotPresentOnSigner,
|
||||||
|
/// A subnet on this certificate is not present on the signer's certificate
|
||||||
|
SubnetNotPresentOnSigner
|
||||||
|
}
|
||||||
|
|
||||||
|
fn net_match(cert_ip: Ipv4Net, root_ips: &Vec<Ipv4Net>) -> bool {
|
||||||
|
for net in root_ips {
|
||||||
|
if net.contains(&cert_ip) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
// Copied from https://github.com/slackhq/nebula/blob/master/cert/cert.proto
|
||||||
|
// with some minimal changes for trifid-pki.
|
||||||
|
syntax = "proto3";
|
||||||
|
package cert_codec;
|
||||||
|
|
||||||
|
message RawNebulaCertificate {
|
||||||
|
RawNebulaCertificateDetails Details = 1;
|
||||||
|
bytes Signature = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message RawNebulaCertificateDetails {
|
||||||
|
string Name = 1;
|
||||||
|
|
||||||
|
// Ips and Subnets are in big endian 32 bit pairs, 1st the ip, 2nd the mask
|
||||||
|
repeated uint32 Ips = 2;
|
||||||
|
repeated uint32 Subnets = 3;
|
||||||
|
|
||||||
|
repeated string Groups = 4;
|
||||||
|
int64 NotBefore = 5;
|
||||||
|
int64 NotAfter = 6;
|
||||||
|
bytes PublicKey = 7;
|
||||||
|
|
||||||
|
bool IsCA = 8;
|
||||||
|
|
||||||
|
// sha-256 of the issuer certificate, if this field is blank the cert is self-signed
|
||||||
|
bytes Issuer = 9;
|
||||||
|
}
|
|
@ -0,0 +1,116 @@
|
||||||
|
// Automatically generated rust module for 'cert_codec.proto' file
|
||||||
|
|
||||||
|
#![allow(non_snake_case)]
|
||||||
|
#![allow(non_upper_case_globals)]
|
||||||
|
#![allow(non_camel_case_types)]
|
||||||
|
#![allow(unused_imports)]
|
||||||
|
#![allow(unknown_lints)]
|
||||||
|
#![allow(clippy::all)]
|
||||||
|
#![cfg_attr(rustfmt, rustfmt_skip)]
|
||||||
|
#![allow(clippy::wildcard_imports)]
|
||||||
|
|
||||||
|
use quick_protobuf::{MessageInfo, MessageRead, MessageWrite, BytesReader, Writer, WriterBackend, Result};
|
||||||
|
use quick_protobuf::sizeofs::*;
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Debug, Default, PartialEq, Clone)]
|
||||||
|
pub struct RawNebulaCertificate {
|
||||||
|
pub Details: Option<cert_codec::RawNebulaCertificateDetails>,
|
||||||
|
pub Signature: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageRead<'a> for RawNebulaCertificate {
|
||||||
|
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
||||||
|
let mut msg = Self::default();
|
||||||
|
while !r.is_eof() {
|
||||||
|
match r.next_tag(bytes) {
|
||||||
|
Ok(10) => msg.Details = Some(r.read_message::<cert_codec::RawNebulaCertificateDetails>(bytes)?),
|
||||||
|
Ok(18) => msg.Signature = r.read_bytes(bytes)?.to_owned(),
|
||||||
|
Ok(t) => { r.read_unknown(bytes, t)?; }
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageWrite for RawNebulaCertificate {
|
||||||
|
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
||||||
|
if let Some(ref s) = self.Details { w.write_with_tag(10, |w| w.write_message(s))?; }
|
||||||
|
if !self.Signature.is_empty() { w.write_with_tag(18, |w| w.write_bytes(&**&self.Signature))?; }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
+ self.Details.as_ref().map_or(0, |m| 1 + sizeof_len((m).get_size()))
|
||||||
|
+ if self.Signature.is_empty() { 0 } else { 1 + sizeof_len((&self.Signature).len()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::derive_partial_eq_without_eq)]
|
||||||
|
#[derive(Debug, Default, PartialEq, Clone)]
|
||||||
|
pub struct RawNebulaCertificateDetails {
|
||||||
|
pub Name: String,
|
||||||
|
pub Ips: Vec<u32>,
|
||||||
|
pub Subnets: Vec<u32>,
|
||||||
|
pub Groups: Vec<String>,
|
||||||
|
pub NotBefore: i64,
|
||||||
|
pub NotAfter: i64,
|
||||||
|
pub PublicKey: Vec<u8>,
|
||||||
|
pub IsCA: bool,
|
||||||
|
pub Issuer: Vec<u8>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> MessageRead<'a> for RawNebulaCertificateDetails {
|
||||||
|
fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> Result<Self> {
|
||||||
|
let mut msg = Self::default();
|
||||||
|
while !r.is_eof() {
|
||||||
|
match r.next_tag(bytes) {
|
||||||
|
Ok(10) => msg.Name = r.read_string(bytes)?.to_owned(),
|
||||||
|
Ok(18) => msg.Ips = r.read_packed(bytes, |r, bytes| Ok(r.read_uint32(bytes)?))?,
|
||||||
|
Ok(26) => msg.Subnets = r.read_packed(bytes, |r, bytes| Ok(r.read_uint32(bytes)?))?,
|
||||||
|
Ok(34) => msg.Groups.push(r.read_string(bytes)?.to_owned()),
|
||||||
|
Ok(40) => msg.NotBefore = r.read_int64(bytes)?,
|
||||||
|
Ok(48) => msg.NotAfter = r.read_int64(bytes)?,
|
||||||
|
Ok(58) => msg.PublicKey = r.read_bytes(bytes)?.to_owned(),
|
||||||
|
Ok(64) => msg.IsCA = r.read_bool(bytes)?,
|
||||||
|
Ok(74) => msg.Issuer = r.read_bytes(bytes)?.to_owned(),
|
||||||
|
Ok(t) => { r.read_unknown(bytes, t)?; }
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::cast_sign_loss)]
|
||||||
|
impl MessageWrite for RawNebulaCertificateDetails {
|
||||||
|
fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> Result<()> {
|
||||||
|
if self.Name != String::default() { w.write_with_tag(10, |w| w.write_string(&**&self.Name))?; }
|
||||||
|
w.write_packed_with_tag(18, &self.Ips, |w, m| w.write_uint32(*m), &|m| sizeof_varint(u64::from(*(m))))?;
|
||||||
|
w.write_packed_with_tag(26, &self.Subnets, |w, m| w.write_uint32(*m), &|m| sizeof_varint(u64::from(*(m))))?;
|
||||||
|
for s in &self.Groups { w.write_with_tag(34, |w| w.write_string(&**s))?; }
|
||||||
|
if self.NotBefore != 0i64 { w.write_with_tag(40, |w| w.write_int64(*&self.NotBefore))?; }
|
||||||
|
if self.NotAfter != 0i64 { w.write_with_tag(48, |w| w.write_int64(*&self.NotAfter))?; }
|
||||||
|
if !self.PublicKey.is_empty() { w.write_with_tag(58, |w| w.write_bytes(&**&self.PublicKey))?; }
|
||||||
|
if self.IsCA != false { w.write_with_tag(64, |w| w.write_bool(*&self.IsCA))?; }
|
||||||
|
if !self.Issuer.is_empty() { w.write_with_tag(74, |w| w.write_bytes(&**&self.Issuer))?; }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_size(&self) -> usize {
|
||||||
|
0
|
||||||
|
+ if self.Name == String::default() { 0 } else { 1 + sizeof_len((&self.Name).len()) }
|
||||||
|
+ if self.Ips.is_empty() { 0 } else { 1 + sizeof_len(self.Ips.iter().map(|s| sizeof_varint(u64::from(*(s)))).sum::<usize>()) }
|
||||||
|
+ if self.Subnets.is_empty() { 0 } else { 1 + sizeof_len(self.Subnets.iter().map(|s| sizeof_varint(u64::from(*(s)))).sum::<usize>()) }
|
||||||
|
+ self.Groups.iter().map(|s| 1 + sizeof_len((s).len())).sum::<usize>()
|
||||||
|
+ if self.NotBefore == 0i64 { 0 } else { 1 + sizeof_varint(*(&self.NotBefore) as u64) }
|
||||||
|
+ if self.NotAfter == 0i64 { 0 } else { 1 + sizeof_varint(*(&self.NotAfter) as u64) }
|
||||||
|
+ if self.PublicKey.is_empty() { 0 } else { 1 + sizeof_len((&self.PublicKey).len()) }
|
||||||
|
+ if self.IsCA == false { 0 } else { 1 + sizeof_varint(u64::from(*(&self.IsCA))) }
|
||||||
|
+ if self.Issuer.is_empty() { 0 } else { 1 + sizeof_len((&self.Issuer).len()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,14 +1,25 @@
|
||||||
pub fn add(left: usize, right: usize) -> usize {
|
//! # trifid-pki
|
||||||
left + right
|
//! trifid-pki is a crate for interacting with the Nebula PKI system. It was created to prevent the need to make constant CLI calls for signing operations in Nebula.
|
||||||
}
|
//! It is designed to be interoperable with the original Go implementation and as such has some oddities with key management to ensure compatability.
|
||||||
|
//!
|
||||||
|
//! This crate has not received any format security audits, however the underlying crates used for actual cryptographic operations (ed25519-dalek and curve25519-dalek) have been audited with no major issues.
|
||||||
|
|
||||||
|
#![warn(clippy::pedantic)]
|
||||||
|
#![warn(clippy::nursery)]
|
||||||
|
#![deny(clippy::unwrap_used)]
|
||||||
|
#![deny(clippy::expect_used)]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![deny(clippy::missing_errors_doc)]
|
||||||
|
#![deny(clippy::missing_panics_doc)]
|
||||||
|
#![deny(clippy::missing_safety_doc)]
|
||||||
|
#![allow(clippy::must_use_candidate)]
|
||||||
|
#![allow(clippy::too_many_lines)]
|
||||||
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
|
|
||||||
|
pub mod ca;
|
||||||
|
pub mod cert;
|
||||||
|
pub(crate) mod cert_codec;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
#[macro_use]
|
||||||
use super::*;
|
pub mod test;
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn it_works() {
|
|
||||||
let result = add(2, 2);
|
|
||||||
assert_eq!(result, 4);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
#![allow(clippy::unwrap_used)]
|
||||||
|
#![allow(clippy::expect_used)]
|
||||||
|
|
||||||
|
use crate::netmask;
|
||||||
|
use std::net::Ipv4Addr;
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
|
use ipnet::Ipv4Net;
|
||||||
|
use crate::cert::{deserialize_nebula_certificate, NebulaCertificate, NebulaCertificateDetails};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn certificate_serialization() {
|
||||||
|
let before = SystemTime::now() - Duration::from_secs(60);
|
||||||
|
let after = SystemTime::now() + Duration::from_secs(60);
|
||||||
|
let pub_key = b"1234567890abcedfghij1234567890ab";
|
||||||
|
|
||||||
|
let cert = NebulaCertificate {
|
||||||
|
details: NebulaCertificateDetails {
|
||||||
|
name: "testing".to_string(),
|
||||||
|
ips: vec![
|
||||||
|
netmask!("10.1.1.1", "255.255.255.0"),
|
||||||
|
netmask!("10.1.1.2", "255.255.0.0"),
|
||||||
|
netmask!("10.1.1.3", "255.0.255.0")
|
||||||
|
],
|
||||||
|
subnets: vec![
|
||||||
|
netmask!("9.1.1.1", "255.0.255.0"),
|
||||||
|
netmask!("9.1.1.2", "255.255.255.0"),
|
||||||
|
netmask!("9.1.1.3", "255.255.0.0")
|
||||||
|
],
|
||||||
|
groups: vec!["test-group1".to_string(), "test-group2".to_string(), "test-group3".to_string()],
|
||||||
|
not_before: before,
|
||||||
|
not_after: after,
|
||||||
|
public_key: *pub_key,
|
||||||
|
is_ca: false,
|
||||||
|
issuer: "1234567890abcedfghij1234567890ab".to_string(),
|
||||||
|
},
|
||||||
|
signature: hex::decode("1234567890abcedfghij1234567890ab").unwrap(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let bytes = cert.serialize().unwrap();
|
||||||
|
|
||||||
|
let deserialized = deserialize_nebula_certificate(&bytes).unwrap();
|
||||||
|
/*
|
||||||
|
assert.Equal(t, nc.Details.Name, nc2.Details.Name)
|
||||||
|
assert.Equal(t, nc.Details.NotBefore, nc2.Details.NotBefore)
|
||||||
|
assert.Equal(t, nc.Details.NotAfter, nc2.Details.NotAfter)
|
||||||
|
assert.Equal(t, nc.Details.PublicKey, nc2.Details.PublicKey)
|
||||||
|
assert.Equal(t, nc.Details.IsCA, nc2.Details.IsCA)
|
||||||
|
*/
|
||||||
|
assert_eq!(cert.signature, deserialized.signature);
|
||||||
|
assert_eq!(cert.details.name, deserialized.details.name);
|
||||||
|
assert_eq!(cert.details.not_before, deserialized.details.not_after);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! netmask {
|
||||||
|
($ip:expr,$mask:expr) => {
|
||||||
|
Ipv4Net::with_netmask(Ipv4Addr::from_str($ip).unwrap(), Ipv4Addr::from_str($mask).unwrap()).unwrap()
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue