diff --git a/Cargo.lock b/Cargo.lock index de0bc8ca..78fefb55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,12 +11,33 @@ dependencies = [ "memchr", ] +[[package]] +name = "async-signature" +version = "0.6.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9bdb5df8dde2bd1ec515a0981636508bb37d55984d0bae3678d4ac859125431" +dependencies = [ + "signature", +] + [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64ct" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" + [[package]] name = "bindgen" version = "0.69.4" @@ -52,6 +73,21 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "block-buffer" +version = "0.11.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd016a0ddc7cb13661bf5576073ce07330a693f8608a1320b4e20561cc12cdc" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + [[package]] name = "cc" version = "1.0.86" @@ -93,6 +129,57 @@ dependencies = [ "cc", ] +[[package]] +name = "cms" +version = "0.3.0-pre.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "956098b1603285c33972193d6f62c8389d3d8548693a4077baa08ff0a8da97c7" +dependencies = [ + "const-oid", + "der", + "spki", + "x509-cert", +] + +[[package]] +name = "const-oid" +version = "0.10.0-rc.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9adcf94f05e094fca3005698822ec791cb4433ced416afda1c5ca3b8dfc05a2f" + +[[package]] +name = "cpufeatures" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-bigint" +version = "0.6.0-rc.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d748d1f5b807ee6d0df5a548d0130417295c3aaed1dcbbb3d6a2e7106e11fcca" +dependencies = [ + "hybrid-array", + "num-traits", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.2.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0b8ce8218c97789f16356e7896b3714f26c2ee1079b79c0b7ae7064bb9089fa" +dependencies = [ + "getrandom", + "hybrid-array", + "rand_core", +] + [[package]] name = "cryptoki" version = "0.7.0" @@ -107,7 +194,30 @@ dependencies = [ "psa-crypto", "secrecy", "serial_test", - "testresult", + "testresult 0.4.1", +] + +[[package]] +name = "cryptoki-rustcrypto" +version = "0.1.0" +dependencies = [ + "cryptoki", + "der", + "ecdsa", + "k256", + "p256", + "p384", + "pkcs12", + "rand", + "rsa", + "serial_test", + "sha1", + "sha2", + "signature", + "spki", + "testresult 0.2.0", + "thiserror", + "x509-cert", ] [[package]] @@ -118,12 +228,83 @@ dependencies = [ "libloading 0.7.4", ] +[[package]] +name = "der" +version = "0.8.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82db698b33305f0134faf590b9d1259dc171b5481ac41d5c8146c3b3ee7d4319" +dependencies = [ + "const-oid", + "der_derive", + "flagset", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "der_derive" +version = "0.8.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "211bea8bb45f5f61bc857104606913ef8ac8b5ec698143aa2aa96a7ffdc94991" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "digest" +version = "0.11.0-pre.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf2e3d6615d99707295a9673e889bf363a04b2a466bd320c65a72536f7577379" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "subtle", +] + +[[package]] +name = "ecdsa" +version = "0.17.0-pre.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e62f2041a28c40b8884b79fbd19bc7457d76c6397767831e9ff4029fc0473a9" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + [[package]] name = "either" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" +[[package]] +name = "elliptic-curve" +version = "0.14.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc43715037532dc2d061e5c97e81b684c28993d52a4fa4eb7d2ce2826d78f2f2" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "group", + "hkdf", + "hybrid-array", + "pem-rfc7468", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "errno" version = "0.3.8" @@ -134,18 +315,74 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + +[[package]] +name = "flagset" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3ea1ec5f8307826a5b71094dd91fc04d4ae75d5709b20ad351c7fb4815c86ec" + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hkdf" +version = "0.13.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00176ff81091018d42ff82e8324f8e5adb0b7e0468d1358f653972562dbff031" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.13.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4b1fb14e4df79f9406b434b60acef9f45c26c50062cccf1346c6103b8c47d58" +dependencies = [ + "digest", +] + [[package]] name = "home" version = "0.5.9" @@ -155,6 +392,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "hybrid-array" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45a9a965bb102c1c891fb017c09a05c965186b1265a207640f323ddd009f9deb" +dependencies = [ + "typenum", + "zeroize", +] + [[package]] name = "instant" version = "0.1.12" @@ -173,11 +420,28 @@ dependencies = [ "either", ] +[[package]] +name = "k256" +version = "0.14.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6995f4341b819603e1b836b530ba1c33bbb677d0a3d68ed122a55081abfc82dd" +dependencies = [ + "cfg-if", + "ecdsa", + "elliptic-curve", + "once_cell", + "sha2", + "signature", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] [[package]] name = "lazycell" @@ -187,9 +451,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.162" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "18d287de67fe55fd7e1581fe933d965a5a9477b38e949cfa9f8574ef01506398" [[package]] name = "libloading" @@ -211,6 +475,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "libm" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa" + [[package]] name = "linux-raw-sys" version = "0.4.13" @@ -255,13 +525,51 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" -version = "0.2.18" +version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -270,6 +578,30 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "p256" +version = "0.14.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71f3fd64a9cad9c26ed7f734b152196d5e56376b9957c832bcca0de48a708080" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + +[[package]] +name = "p384" +version = "0.14.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e19554fe6ee269c860a0f231cbba714e5cbef26a927c75d8e30ac9040a4b32e" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parking_lot" version = "0.11.2" @@ -301,6 +633,57 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +[[package]] +name = "pem-rfc7468" +version = "1.0.0-rc.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2dfbfa5c6f0906884269722c5478e72fd4d6c0e24fe600332c6d62359567ce1" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pkcs1" +version = "0.8.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "226eb25e2c46c166ce498ac0f606ac623142d640064879ff445938accddff1e2" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs12" +version = "0.2.0-pre" +source = "git+https://github.com/RustCrypto/formats.git#48eac00018b31215141f98550e342da7d40062d6" +dependencies = [ + "cms", + "const-oid", + "der", + "spki", + "x509-cert", +] + +[[package]] +name = "pkcs8" +version = "0.11.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eacd2c7141f32aef1cfd1ad0defb5287a3d94592d7ab57c1ae20e3f9f1f0db1f" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + [[package]] name = "prettyplease" version = "0.2.16" @@ -311,6 +694,15 @@ dependencies = [ "syn 2.0.50", ] +[[package]] +name = "primeorder" +version = "0.14.0-pre.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794117b388378d55629f78f61e64e182baa200bf59c1a8205e0c46508ce5873" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro2" version = "1.0.78" @@ -353,6 +745,36 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "redox_syscall" version = "0.2.16" @@ -391,6 +813,35 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +[[package]] +name = "rfc6979" +version = "0.5.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871ee76a3eee98b0f805e5d1caf26929f4565073c580c053a55f886fc15dea49" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rsa" +version = "0.10.0-pre.3" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "sha2", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -425,6 +876,20 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.8.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1988446eff153796413a73669dfaa4caa3f5ce8b25fac89e3821a39c611772e" +dependencies = [ + "base16ct", + "der", + "hybrid-array", + "pkcs8", + "subtle", + "zeroize", +] + [[package]] name = "secrecy" version = "0.8.0" @@ -477,18 +942,84 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "sha1" +version = "0.11.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9540978cef7a8498211c1b1c14e5ce920fe5bd524ea84f4a3d72d4602515ae93" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.11.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "540c0893cce56cdbcfebcec191ec8e0f470dd1889b6e7a0b503e310a94a168f5" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" +[[package]] +name = "signature" +version = "2.3.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "054d71959c7051b9042c26af337f05cc930575ed2604d7d3ced3158383e59734" +dependencies = [ + "digest", + "rand_core", + "signature_derive", +] + +[[package]] +name = "signature_derive" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0381d1913eeaf4c7bc4094016c9a8de6c1120663afe32a90ff268ad7f80486" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + [[package]] name = "smallvec" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "spki" +version = "0.8.0-rc.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37ac66481418fd7afdc584adcf3be9aa572cf6c2858814494dc2a01755f050bc" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -511,12 +1042,65 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "testresult" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "649113ab56eab59f78f02314a05b24bda0200d322c9eb1c60d0af8554e94c5ef" + [[package]] name = "testresult" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614b328ff036a4ef882c61570f72918f7e9c5bee1da33f8e7f91e01daee7e56c" +[[package]] +name = "thiserror" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d11abd9594d9b38965ef50805c5e469ca9cc6f197f883f717e0269a3057b3d5" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae71770322cbd277e69d762a16c444af02aa0575ac0d174f0b9562d3b37f8602" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "tls_codec" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e78c9c330f8c85b2bae7c8368f2739157db9991235123aa1b15ef9502bfb6a" +dependencies = [ + "tls_codec_derive", + "zeroize", +] + +[[package]] +name = "tls_codec_derive" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9ef545650e79f30233c0003bcc2504d7efac6dad25fca40744de773fe2049c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -533,6 +1117,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "which" version = "4.4.2" @@ -708,11 +1298,47 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "x509-cert" +version = "0.3.0-pre.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2db382aa43c1fb5c419a960f72c3847ab0f383f635fc2e25f0bd6c5fb94371d1" +dependencies = [ + "async-signature", + "const-oid", + "der", + "sha1", + "signature", + "spki", + "tls_codec", +] + +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "byteorder", + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] diff --git a/Cargo.toml b/Cargo.toml index 99938e6c..9a3f5d3a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,2 +1,6 @@ [workspace] -members = ["cryptoki", "cryptoki-sys"] +members = ["cryptoki", "cryptoki-sys", "cryptoki-rustcrypto"] + +[patch.crates-io] +pkcs12 = { git = "https://github.com/RustCrypto/formats.git" } +rsa = { path = "../RSA" } diff --git a/cryptoki-rustcrypto/Cargo.toml b/cryptoki-rustcrypto/Cargo.toml new file mode 100644 index 00000000..8d47dc9c --- /dev/null +++ b/cryptoki-rustcrypto/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "cryptoki-rustcrypto" +version = "0.1.0" +edition = "2018" +authors = ["Contributors to the Parsec project"] +description = "Compatibility layer from PKCS #11 to the RustCrypto ecosystem" +readme = "README.md" +keywords = ["pkcs11", "cryptoki", "hsm"] +categories = ["cryptography", "hardware-support"] +license = "Apache-2.0" +repository = "https://github.com/parallaxsecond/rust-cryptoki" + +[dependencies] +cryptoki = { path = "../cryptoki", version = "0.7.0" } +der = "=0.8.0-rc.1" +ecdsa = "=0.17.0-pre.9" +#p224 = { version = "=0.14.0-pre.2", features = ["pkcs8"] } +p256 = { version = "=0.14.0-pre.2", features = ["pkcs8"] } +p384 = { version = "=0.14.0-pre.2", features = ["pkcs8"] } +k256 = { version = "=0.14.0-pre.2", features = ["pkcs8"] } +pkcs12 = { version = "=0.2.0-pre" } +rsa = { version = "=0.10.0-pre.3", features = ["sha2"] } +signature = { version = "=2.3.0-pre.4", features = ["derive", "digest"] } +sha1 = { version = "=0.11.0-pre.4", features = ["oid"] } +sha2 = { version = "=0.11.0-pre.4", features = ["oid"] } +spki = "=0.8.0-rc.1" +x509-cert = "=0.3.0-pre.0" +thiserror = "1.0" + +[dev-dependencies] +rand = "0.8.5" +serial_test = "0.5.1" +testresult = "0.2.0" +x509-cert = { version = "=0.3.0-pre.0", features = ["builder"] } diff --git a/cryptoki-rustcrypto/src/ecdsa.rs b/cryptoki-rustcrypto/src/ecdsa.rs new file mode 100644 index 00000000..d9a0b0fc --- /dev/null +++ b/cryptoki-rustcrypto/src/ecdsa.rs @@ -0,0 +1,278 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use cryptoki::{ + mechanism::Mechanism, + object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle}, +}; +use der::{ + asn1::{ObjectIdentifier, OctetString, OctetStringRef}, + oid::AssociatedOid, + AnyRef, Decode, Encode, +}; +use ecdsa::{ + elliptic_curve::{ + array::ArraySize, + ops::Invert, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + subtle::CtOption, + AffinePoint, CurveArithmetic, FieldBytesSize, PublicKey, Scalar, SecretKey, + }, + hazmat::DigestPrimitive, + PrimeCurve, Signature, SignatureSize, VerifyingKey, +}; +use signature::{digest::Digest, DigestSigner}; +use spki::{ + AlgorithmIdentifier, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, + SignatureAlgorithmIdentifier, +}; +use std::{convert::TryFrom, ops::Add}; +use thiserror::Error; + +use crate::{CryptokiImport, SessionLike}; + +pub fn read_key( + session: &S, + template: impl Into>, +) -> Result, Error> +where + FieldBytesSize: ModulusSize, + AffinePoint: FromEncodedPoint + ToEncodedPoint, +{ + let mut template = template.into(); + template.push(Attribute::Class(ObjectClass::PUBLIC_KEY)); + template.push(Attribute::KeyType(KeyType::EC)); + template.push(Attribute::EcParams(C::OID.to_der().unwrap())); + + let keys = session.find_objects(&template)?; + if let Some(public_key) = keys.first() { + let attribute_pub = session.get_attributes(*public_key, &[AttributeType::EcPoint])?; + + let mut ec_point = None; + for attribute in attribute_pub { + match attribute { + Attribute::EcPoint(p) if ec_point.is_none() => { + ec_point = Some(p); + break; + } + _ => {} + } + } + + let ec_point = ec_point.ok_or(Error::MissingAttribute(AttributeType::EcPoint))?; + + // documented as "DER-encoding of ANSI X9.62 ECPoint value Q" + // https://docs.oasis-open.org/pkcs11/pkcs11-spec/v3.1/os/pkcs11-spec-v3.1-os.html#_Toc111203418 + // https://www.rfc-editor.org/rfc/rfc5480#section-2.2 + let ec_point = OctetStringRef::from_der(&ec_point).unwrap(); + + Ok(PublicKey::::from_sec1_bytes(ec_point.as_bytes())?) + } else { + Err(Error::MissingKey) + } +} + +impl CryptokiImport for SecretKey +where + C: PrimeCurve + CurveArithmetic, + Scalar: Invert>>, + SignatureSize: ArraySize, + + C: AssociatedOid, +{ + fn put_key( + &self, + session: &S, + template: impl Into>, + ) -> cryptoki::error::Result { + let mut template = template.into(); + template.push(Attribute::Class(ObjectClass::PRIVATE_KEY)); + template.push(Attribute::KeyType(KeyType::EC)); + template.push(Attribute::EcParams(C::OID.to_der().unwrap())); + template.push(Attribute::Value(self.to_bytes().as_slice().to_vec())); + + let handle = session.create_object(&template)?; + + Ok(handle) + } +} + +impl CryptokiImport for PublicKey +where + C: PrimeCurve + CurveArithmetic + PointCompression, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, + C: AssociatedOid, +{ + fn put_key( + &self, + session: &S, + template: impl Into>, + ) -> cryptoki::error::Result { + let mut template = template.into(); + template.push(Attribute::Class(ObjectClass::PUBLIC_KEY)); + template.push(Attribute::KeyType(KeyType::EC)); + template.push(Attribute::EcParams(C::OID.to_der().unwrap())); + let ec_point = OctetString::new(self.to_sec1_bytes()).unwrap(); + template.push(Attribute::EcPoint(ec_point.to_der().unwrap())); + + let handle = session.create_object(&template)?; + + Ok(handle) + } +} + +#[derive(Error, Debug)] +pub enum Error { + #[error("Cryptoki error: {0}")] + Cryptoki(#[from] cryptoki::error::Error), + + #[error("Private key missing attribute: {0}")] + MissingAttribute(AttributeType), + + #[error("Elliptic curve error: {0}")] + Ecdsa(#[from] ecdsa::elliptic_curve::Error), + + #[error("Key not found")] + MissingKey, +} + +pub trait SignAlgorithm: PrimeCurve + CurveArithmetic + AssociatedOid + DigestPrimitive { + fn sign_mechanism() -> Mechanism<'static>; +} + +macro_rules! impl_sign_algorithm { + ($ec:ty) => { + impl SignAlgorithm for $ec { + fn sign_mechanism() -> Mechanism<'static> { + Mechanism::Ecdsa + } + } + }; +} + +//impl_sign_algorithm!(p224::NistP224); +impl_sign_algorithm!(p256::NistP256); +impl_sign_algorithm!(p384::NistP384); +impl_sign_algorithm!(k256::Secp256k1); + +#[derive(signature::Signer)] +pub struct Signer { + session: S, + private_key: ObjectHandle, + verifying_key: VerifyingKey, +} + +impl Signer +where + FieldBytesSize: ModulusSize, + AffinePoint: FromEncodedPoint + ToEncodedPoint, +{ + pub fn new(session: S, label: &[u8]) -> Result { + // First we'll lookup a private key with that label. + let template = vec![ + Attribute::Label(label.to_vec()), + Attribute::Class(ObjectClass::PRIVATE_KEY), + Attribute::KeyType(KeyType::EC), + Attribute::EcParams(C::OID.to_der().unwrap()), + Attribute::Sign(true), + ]; + + let private_key = session.find_objects(&template)?.remove(0); + let attribute_priv = session.get_attributes(private_key, &[AttributeType::Id])?; + + // Second we'll lookup a public key with the same label/ec params/ec point + let mut template = vec![Attribute::Private(false), Attribute::Label(label.to_vec())]; + let mut id = None; + for attribute in attribute_priv { + match attribute { + Attribute::Id(i) if id.is_none() => { + template.push(Attribute::Id(i.clone())); + id = Some(i); + } + _ => {} + } + } + + id.ok_or(Error::MissingAttribute(AttributeType::Id))?; + + let public = read_key(&session, template)?; + let verifying_key = public.into(); + + Ok(Self { + session, + private_key, + verifying_key, + }) + } + + pub fn into_session(self) -> S { + self.session + } +} + +impl AssociatedAlgorithmIdentifier for Signer +where + C: AssociatedOid, +{ + type Params = ObjectIdentifier; + + const ALGORITHM_IDENTIFIER: AlgorithmIdentifier = + PublicKey::::ALGORITHM_IDENTIFIER; +} + +impl signature::Keypair for Signer { + type VerifyingKey = VerifyingKey; + + fn verifying_key(&self) -> Self::VerifyingKey { + self.verifying_key + } +} + +impl DigestSigner> for Signer +where + <::FieldBytesSize as Add>::Output: ArraySize, +{ + fn try_sign_digest(&self, digest: C::Digest) -> Result, signature::Error> { + let msg = digest.finalize(); + + let bytes = self + .session + .sign(&C::sign_mechanism(), self.private_key, &msg) + .map_err(Error::Cryptoki) + .map_err(Box::new) + .map_err(signature::Error::from_source)?; + + let signature = Signature::try_from(bytes.as_slice())?; + + Ok(signature) + } +} + +impl SignatureAlgorithmIdentifier for Signer +where + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, + Signature: AssociatedAlgorithmIdentifier>, +{ + type Params = AnyRef<'static>; + + const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = + Signature::::ALGORITHM_IDENTIFIER; +} + +impl DigestSigner> + for Signer +where + ecdsa::der::MaxSize: ArraySize, + as Add>::Output: Add + ArraySize, + Self: DigestSigner>, +{ + fn try_sign_digest( + &self, + digest: C::Digest, + ) -> Result, signature::Error> { + DigestSigner::>::try_sign_digest(self, digest).map(Into::into) + } +} diff --git a/cryptoki-rustcrypto/src/lib.rs b/cryptoki-rustcrypto/src/lib.rs new file mode 100644 index 00000000..de5a3d47 --- /dev/null +++ b/cryptoki-rustcrypto/src/lib.rs @@ -0,0 +1,126 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use cryptoki::{ + error::Result, + mechanism::Mechanism, + object::{Attribute, AttributeType, ObjectHandle}, + session::{Session, UserType}, + types::AuthPin, +}; + +pub use cryptoki; + +pub mod ecdsa; +pub mod rng; +pub mod rsa; +pub mod x509; + +pub trait SessionLike { + fn create_object(&self, template: &[Attribute]) -> Result; + fn find_objects(&self, template: &[Attribute]) -> Result>; + fn get_attributes( + &self, + object: ObjectHandle, + attributes: &[AttributeType], + ) -> Result>; + fn update_attributes(&self, object: ObjectHandle, template: &[Attribute]) -> Result<()>; + fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result>; + fn generate_random_slice(&self, random_data: &mut [u8]) -> Result<()>; + fn wrap_key( + &self, + mechanism: &Mechanism, + wrapping_key: ObjectHandle, + key: ObjectHandle, + ) -> Result>; + fn login(&self, user_type: UserType, pin: Option<&AuthPin>) -> Result<()>; + fn logout(&self) -> Result<()>; +} + +impl SessionLike for Session { + fn create_object(&self, template: &[Attribute]) -> Result { + Session::create_object(self, template) + } + fn find_objects(&self, template: &[Attribute]) -> Result> { + Session::find_objects(self, template) + } + fn get_attributes( + &self, + object: ObjectHandle, + attributes: &[AttributeType], + ) -> Result> { + Session::get_attributes(self, object, attributes) + } + fn update_attributes(&self, object: ObjectHandle, template: &[Attribute]) -> Result<()> { + Session::update_attributes(self, object, template) + } + + fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result> { + Session::sign(self, mechanism, key, data) + } + fn generate_random_slice(&self, random_data: &mut [u8]) -> Result<()> { + Session::generate_random_slice(self, random_data) + } + fn wrap_key( + &self, + mechanism: &Mechanism, + wrapping_key: ObjectHandle, + key: ObjectHandle, + ) -> Result> { + Session::wrap_key(self, mechanism, wrapping_key, key) + } + fn login(&self, user_type: UserType, pin: Option<&AuthPin>) -> Result<()> { + Session::login(self, user_type, pin) + } + fn logout(&self) -> Result<()> { + Session::logout(self) + } +} + +impl<'s> SessionLike for &'s Session { + fn create_object(&self, template: &[Attribute]) -> Result { + Session::create_object(self, template) + } + fn find_objects(&self, template: &[Attribute]) -> Result> { + Session::find_objects(self, template) + } + fn get_attributes( + &self, + object: ObjectHandle, + attributes: &[AttributeType], + ) -> Result> { + Session::get_attributes(self, object, attributes) + } + fn update_attributes(&self, object: ObjectHandle, template: &[Attribute]) -> Result<()> { + Session::update_attributes(self, object, template) + } + + fn sign(&self, mechanism: &Mechanism, key: ObjectHandle, data: &[u8]) -> Result> { + Session::sign(self, mechanism, key, data) + } + fn generate_random_slice(&self, random_data: &mut [u8]) -> Result<()> { + Session::generate_random_slice(self, random_data) + } + fn wrap_key( + &self, + mechanism: &Mechanism, + wrapping_key: ObjectHandle, + key: ObjectHandle, + ) -> Result> { + Session::wrap_key(self, mechanism, wrapping_key, key) + } + fn login(&self, user_type: UserType, pin: Option<&AuthPin>) -> Result<()> { + Session::login(self, user_type, pin) + } + fn logout(&self) -> Result<()> { + Session::logout(self) + } +} + +pub trait CryptokiImport { + fn put_key( + &self, + session: &S, + template: impl Into>, + ) -> Result; +} diff --git a/cryptoki-rustcrypto/src/rng.rs b/cryptoki-rustcrypto/src/rng.rs new file mode 100644 index 00000000..551f6415 --- /dev/null +++ b/cryptoki-rustcrypto/src/rng.rs @@ -0,0 +1,55 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use signature::rand_core::{CryptoRng, Error as RndError, RngCore}; +use thiserror::Error; + +use crate::SessionLike; + +#[derive(Debug, Error)] +pub enum Error {} + +/// [`Rng`] is a PKCS#11-backed CSPRNG. +/// +/// ## Panics +/// +/// The [`RngCore::fill_bytes`] implementation may panic if the provider was +/// unable to return enough bytes. +pub struct Rng(S); + +// TODO(baloo): check for CKF_RNG bit flag (CK_TOKEN_INFO struct -> flags) +impl Rng { + pub fn new(session: S) -> Result { + Ok(Self(session)) + } +} + +macro_rules! impl_next_uint { + ($self:ident, $u:ty) => {{ + let mut buf = <$u>::MIN.to_be_bytes(); + $self.fill_bytes(&mut buf[..]); + + <$u>::from_be_bytes(buf) + }}; +} + +impl RngCore for Rng { + fn next_u32(&mut self) -> u32 { + impl_next_uint!(self, u32) + } + + fn next_u64(&mut self) -> u64 { + impl_next_uint!(self, u64) + } + + fn fill_bytes(&mut self, dest: &mut [u8]) { + self.try_fill_bytes(dest) + .expect("Cryptoki provider failed to generate random"); + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), RndError> { + self.0.generate_random_slice(dest).map_err(RndError::new) + } +} + +impl CryptoRng for Rng {} diff --git a/cryptoki-rustcrypto/src/rsa/mod.rs b/cryptoki-rustcrypto/src/rsa/mod.rs new file mode 100644 index 00000000..ee6d0c6b --- /dev/null +++ b/cryptoki-rustcrypto/src/rsa/mod.rs @@ -0,0 +1,146 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use cryptoki::{ + mechanism::{ + rsa::{PkcsMgfType, PkcsPssParams}, + Mechanism, MechanismType, + }, + object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle}, +}; +use der::oid::AssociatedOid; +use rsa::{traits::PublicKeyParts, BigUint, RsaPublicKey}; +use signature::digest::Digest; +use std::convert::TryInto; +use thiserror::Error; + +use crate::{CryptokiImport, SessionLike}; + +pub mod pkcs1v15; +pub mod pss; + +pub fn read_key( + session: &S, + template: impl Into>, +) -> Result { + let mut template: Vec = template.into(); + template.push(Attribute::Class(ObjectClass::PUBLIC_KEY)); + template.push(Attribute::KeyType(KeyType::RSA)); + + let keys = session.find_objects(&template)?; + if let Some(key) = keys.first() { + let attribute_priv = session.get_attributes( + *key, + &[AttributeType::Modulus, AttributeType::PublicExponent], + )?; + + let mut modulus = None; + let mut public_exponent = None; + + for attribute in attribute_priv { + match attribute { + Attribute::Modulus(m) if modulus.is_none() => { + modulus = Some(m.clone()); + } + Attribute::PublicExponent(e) if public_exponent.is_none() => { + public_exponent = Some(e.clone()); + } + _ => {} + } + } + + let modulus = modulus + .ok_or(Error::MissingAttribute(AttributeType::Modulus)) + .map(|v| BigUint::from_bytes_be(v.as_slice()))?; + let public_exponent = public_exponent + .ok_or(Error::MissingAttribute(AttributeType::PublicExponent)) + .map(|v| BigUint::from_bytes_be(v.as_slice()))?; + + Ok(RsaPublicKey::new(modulus, public_exponent)?) + } else { + Err(Error::MissingKey) + } +} + +#[derive(Debug, Error)] +pub enum Error { + #[error("Cryptoki error: {0}")] + Cryptoki(#[from] cryptoki::error::Error), + + #[error("Private key missing attribute: {0}")] + MissingAttribute(AttributeType), + + #[error("RSA error: {0}")] + Rsa(#[from] rsa::Error), + + #[error("Key not found")] + MissingKey, +} + +pub trait DigestSigning: Digest + AssociatedOid { + fn pkcs_mechanism() -> Mechanism<'static>; + + fn pss_mechanism() -> Mechanism<'static>; +} + +macro_rules! impl_digest_signing { + ($d:ty, $pkcs_mech:ident, $pss_mech:ident, $mt:ident, $mgf:ident) => { + impl DigestSigning for $d { + fn pkcs_mechanism() -> Mechanism<'static> { + Mechanism::$pkcs_mech + } + + fn pss_mechanism() -> Mechanism<'static> { + Mechanism::$pss_mech(PkcsPssParams { + hash_alg: MechanismType::$mt, + mgf: PkcsMgfType::$mgf, + // Safety: + // the output_size of an hash function will not go over 2^32, + // this unwrap is safe. + s_len: Self::output_size().try_into().unwrap(), + }) + } + } + }; +} + +impl_digest_signing!(sha1::Sha1, Sha1RsaPkcs, Sha1RsaPkcsPss, SHA1, MGF1_SHA1); +impl_digest_signing!( + sha2::Sha256, + Sha256RsaPkcs, + Sha256RsaPkcsPss, + SHA256, + MGF1_SHA256 +); +impl_digest_signing!( + sha2::Sha384, + Sha384RsaPkcs, + Sha384RsaPkcsPss, + SHA384, + MGF1_SHA384 +); +impl_digest_signing!( + sha2::Sha512, + Sha512RsaPkcs, + Sha512RsaPkcsPss, + SHA512, + MGF1_SHA512 +); + +impl CryptokiImport for RsaPublicKey { + fn put_key( + &self, + session: &S, + template: impl Into>, + ) -> cryptoki::error::Result { + let mut template = template.into(); + template.push(Attribute::Class(ObjectClass::PUBLIC_KEY)); + template.push(Attribute::KeyType(KeyType::RSA)); + template.push(Attribute::Modulus(self.n().to_bytes_be())); + template.push(Attribute::PublicExponent(self.e().to_bytes_be())); + + let handle = session.create_object(&template)?; + + Ok(handle) + } +} diff --git a/cryptoki-rustcrypto/src/rsa/pkcs1v15.rs b/cryptoki-rustcrypto/src/rsa/pkcs1v15.rs new file mode 100644 index 00000000..71f288c2 --- /dev/null +++ b/cryptoki-rustcrypto/src/rsa/pkcs1v15.rs @@ -0,0 +1,150 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use cryptoki::{ + mechanism::Mechanism, + object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle}, +}; +use rsa::pkcs1v15::{RsaSignatureAssociatedOid, Signature, VerifyingKey}; +use spki::{ + der::{asn1::OctetString, oid::AssociatedOid, referenced::RefToOwned, AnyRef, Encode}, + AlgorithmIdentifier, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, + SignatureAlgorithmIdentifier, +}; +use std::convert::TryFrom; + +use super::{read_key, DigestSigning, Error}; +use crate::SessionLike; + +pub struct Signer { + session: S, + private_key: ObjectHandle, + verifying_key: VerifyingKey, +} + +impl Signer { + pub fn new(session: S, label: &[u8]) -> Result { + // First we'll lookup a private key with that label. + let template = vec![ + Attribute::Token(true), + Attribute::Private(true), + Attribute::Label(label.to_vec()), + Attribute::Class(ObjectClass::PRIVATE_KEY), + Attribute::KeyType(KeyType::RSA), + Attribute::Sign(true), + ]; + + let private_key = session.find_objects(&template)?.remove(0); + let attribute_priv = session.get_attributes( + private_key, + &[AttributeType::Modulus, AttributeType::PublicExponent], + )?; + + // Second we'll lookup a public key with the same label/modulus/public exponent + let mut template = vec![Attribute::Private(false), Attribute::Label(label.to_vec())]; + let mut modulus = None; + let mut public_exponent = None; + for attribute in attribute_priv { + match attribute { + Attribute::Modulus(m) if modulus.is_none() => { + modulus = Some(m.clone()); + template.push(Attribute::Modulus(m)); + } + Attribute::PublicExponent(e) if public_exponent.is_none() => { + public_exponent = Some(e.clone()); + template.push(Attribute::PublicExponent(e)); + } + _ => {} + } + } + + let public_key = read_key(&session, template)?; + + let verifying_key = VerifyingKey::new(public_key); + + Ok(Self { + session, + private_key, + verifying_key, + }) + } + + pub fn into_session(self) -> S { + self.session + } +} + +impl AssociatedAlgorithmIdentifier for Signer +where + D: DigestSigning, + S: SessionLike, +{ + type Params = as AssociatedAlgorithmIdentifier>::Params; + const ALGORITHM_IDENTIFIER: AlgorithmIdentifier = + as AssociatedAlgorithmIdentifier>::ALGORITHM_IDENTIFIER; +} + +impl signature::Keypair for Signer { + type VerifyingKey = VerifyingKey; + + fn verifying_key(&self) -> Self::VerifyingKey { + self.verifying_key.clone() + } +} + +impl signature::Signer for Signer { + fn try_sign(&self, msg: &[u8]) -> Result { + let bytes = self + .session + .sign(&D::pkcs_mechanism(), self.private_key, msg) + .map_err(Error::Cryptoki) + .map_err(Box::new) + .map_err(signature::Error::from_source)?; + + let signature = Signature::try_from(bytes.as_slice())?; + + Ok(signature) + } +} + +impl SignatureAlgorithmIdentifier for Signer +where + S: SessionLike, + D: DigestSigning + RsaSignatureAssociatedOid, +{ + type Params = as SignatureAlgorithmIdentifier>::Params; + + const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier = + as SignatureAlgorithmIdentifier>::SIGNATURE_ALGORITHM_IDENTIFIER; +} + +impl signature::hazmat::PrehashSigner for Signer +where + S: SessionLike, + D: DigestSigning + RsaSignatureAssociatedOid, +{ + fn sign_prehash(&self, prehash: &[u8]) -> Result { + let payload = pkcs12::DigestInfo { + algorithm: (AlgorithmIdentifierRef { + oid: ::OID, + parameters: Some(AnyRef::NULL), + }) + .ref_to_owned(), + digest: OctetString::new(prehash).unwrap(), + }; + + let msg = payload.to_der().unwrap(); + println!("msg: {msg:x?}"); + + let bytes = self + .session + .sign(&Mechanism::RsaPkcs, self.private_key, &msg) + .map_err(Error::Cryptoki) + .map_err(Box::new) + .map_err(signature::Error::from_source)?; + + let signature = Signature::try_from(bytes.as_slice())?; + + Ok(signature) + } +} diff --git a/cryptoki-rustcrypto/src/rsa/pss.rs b/cryptoki-rustcrypto/src/rsa/pss.rs new file mode 100644 index 00000000..303248f8 --- /dev/null +++ b/cryptoki-rustcrypto/src/rsa/pss.rs @@ -0,0 +1,110 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use cryptoki::object::{Attribute, AttributeType, KeyType, ObjectClass, ObjectHandle}; +use der::AnyRef; +use rsa::{ + pkcs1, + pkcs8::{self}, + pss::{get_default_pss_signature_algo_id, Signature, VerifyingKey}, +}; +use spki::{ + AlgorithmIdentifierOwned, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier, + DynSignatureAlgorithmIdentifier, +}; +use std::convert::TryFrom; + +use super::{read_key, DigestSigning, Error}; +use crate::SessionLike; + +pub struct Signer { + session: S, + private_key: ObjectHandle, + verifying_key: VerifyingKey, +} + +impl Signer { + pub fn new(session: S, label: &[u8]) -> Result { + // First we'll lookup a private key with that label. + let template = vec![ + Attribute::Token(true), + Attribute::Private(true), + Attribute::Label(label.to_vec()), + Attribute::Class(ObjectClass::PRIVATE_KEY), + Attribute::KeyType(KeyType::RSA), + Attribute::Sign(true), + ]; + + let private_key = session.find_objects(&template)?.remove(0); + let attribute_priv = session.get_attributes( + private_key, + &[AttributeType::Modulus, AttributeType::PublicExponent], + )?; + + // Second we'll lookup a public key with the same label/modulus/public exponent + let mut template = vec![Attribute::Private(false), Attribute::Label(label.to_vec())]; + let mut modulus = None; + let mut public_exponent = None; + for attribute in attribute_priv { + match attribute { + Attribute::Modulus(m) if modulus.is_none() => { + modulus = Some(m.clone()); + template.push(Attribute::Modulus(m)); + } + Attribute::PublicExponent(e) if public_exponent.is_none() => { + public_exponent = Some(e.clone()); + template.push(Attribute::PublicExponent(e)); + } + _ => {} + } + } + + let public_key = read_key(&session, template)?; + + let verifying_key = VerifyingKey::new(public_key); + + Ok(Self { + session, + private_key, + verifying_key, + }) + } + + pub fn into_session(self) -> S { + self.session + } +} + +impl AssociatedAlgorithmIdentifier for Signer { + type Params = AnyRef<'static>; + const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = pkcs1::ALGORITHM_ID; +} + +impl signature::Keypair for Signer { + type VerifyingKey = VerifyingKey; + + fn verifying_key(&self) -> Self::VerifyingKey { + self.verifying_key.clone() + } +} + +impl signature::Signer for Signer { + fn try_sign(&self, msg: &[u8]) -> Result { + let bytes = self + .session + .sign(&D::pss_mechanism(), self.private_key, msg) + .map_err(Error::Cryptoki) + .map_err(Box::new) + .map_err(signature::Error::from_source)?; + + let signature = Signature::try_from(bytes.as_slice())?; + + Ok(signature) + } +} + +impl DynSignatureAlgorithmIdentifier for Signer { + fn signature_algorithm_identifier(&self) -> pkcs8::spki::Result { + get_default_pss_signature_algo_id::() + } +} diff --git a/cryptoki-rustcrypto/src/x509.rs b/cryptoki-rustcrypto/src/x509.rs new file mode 100644 index 00000000..e3cf4d65 --- /dev/null +++ b/cryptoki-rustcrypto/src/x509.rs @@ -0,0 +1,97 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use cryptoki::object::{Attribute, AttributeType, CertificateType, ObjectClass, ObjectHandle}; +use thiserror::Error; +use x509_cert::{ + certificate::{CertificateInner, Profile}, + der::{Decode, Encode}, +}; + +use crate::SessionLike; + +#[derive(Debug, Error)] +pub enum Error { + #[error("Cryptoki error: {0}")] + Cryptoki(#[from] cryptoki::error::Error), + + #[error("Missing attribute: {0}")] + MissingAttribute(AttributeType), + + #[error(transparent)] + Der(#[from] x509_cert::der::Error), + + #[error("No such certificate found")] + MissingCert, +} + +pub trait CertPkcs11 { + fn pkcs11_store>>( + &self, + session: &S, + base_template: T, + ) -> Result; + + fn pkcs11_load>>( + session: &S, + template: T, + ) -> Result + where + Self: Sized; +} + +impl

CertPkcs11 for CertificateInner

+where + P: Profile, +{ + fn pkcs11_store>>( + &self, + session: &S, + base_template: T, + ) -> Result { + let mut template = base_template.into(); + template.push(Attribute::Class(ObjectClass::CERTIFICATE)); + template.push(Attribute::CertificateType(CertificateType::X_509)); + template.push(Attribute::Token(true)); + template.push(Attribute::Value(self.to_der()?)); + if !self.tbs_certificate().subject().is_empty() { + template.push(Attribute::Subject( + self.tbs_certificate().subject().to_der()?, + )); + } + + Ok(session.create_object(&template)?) + } + + fn pkcs11_load>>( + session: &S, + template: T, + ) -> Result { + let mut template = template.into(); + template.push(Attribute::Class(ObjectClass::CERTIFICATE)); + template.push(Attribute::CertificateType(CertificateType::X_509)); + + let certs = session.find_objects(&template)?; + if let Some(cert) = certs.first() { + let attributes = session.get_attributes(*cert, &[AttributeType::Value])?; + + let mut value = None; + for attribute in attributes { + match attribute { + Attribute::Value(v) if value.is_none() => { + value = Some(v); + } + _ => {} + } + } + + let value = value.ok_or(Error::MissingAttribute(AttributeType::Value))?; + + let cert = Self::from_der(&value)?; + + Ok(cert) + } else { + Err(Error::MissingCert) + } + } +} diff --git a/cryptoki-rustcrypto/tests/common.rs b/cryptoki-rustcrypto/tests/common.rs new file mode 100644 index 00000000..9da0703b --- /dev/null +++ b/cryptoki-rustcrypto/tests/common.rs @@ -0,0 +1,44 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +use cryptoki::context::{CInitializeArgs, Pkcs11}; +use cryptoki::session::UserType; +use cryptoki::slot::Slot; +use cryptoki::types::AuthPin; +use std::env; + +// The default user pin +pub static USER_PIN: &str = "fedcba"; +// The default SO pin +pub static SO_PIN: &str = "abcdef"; + +pub fn get_pkcs11() -> Pkcs11 { + Pkcs11::new( + env::var("PKCS11_SOFTHSM2_MODULE") + .unwrap_or_else(|_| "/usr/local/lib/softhsm/libsofthsm2.so".to_string()), + ) + .unwrap() +} + +pub fn init_pins() -> (Pkcs11, Slot) { + let pkcs11 = get_pkcs11(); + + // initialize the library + pkcs11.initialize(CInitializeArgs::OsThreads).unwrap(); + + // find a slot, get the first one + let slot = pkcs11.get_slots_with_token().unwrap().remove(0); + + let so_pin = AuthPin::new(SO_PIN.into()); + pkcs11.init_token(slot, &so_pin, "Test Token").unwrap(); + + { + // open a session + let session = pkcs11.open_rw_session(slot).unwrap(); + // log in the session + session.login(UserType::So, Some(&so_pin)).unwrap(); + session.init_pin(&AuthPin::new(USER_PIN.into())).unwrap(); + } + + (pkcs11, slot) +} diff --git a/cryptoki-rustcrypto/tests/ecdsa.rs b/cryptoki-rustcrypto/tests/ecdsa.rs new file mode 100644 index 00000000..35d35fdf --- /dev/null +++ b/cryptoki-rustcrypto/tests/ecdsa.rs @@ -0,0 +1,118 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +mod common; + +use crate::common::USER_PIN; +use common::init_pins; +use cryptoki::{ + mechanism::Mechanism, + object::{Attribute, KeyType}, + session::UserType, + types::AuthPin, +}; +use cryptoki_rustcrypto::{ecdsa, CryptokiImport}; +use der::Encode; +use p256::pkcs8::AssociatedOid; +use serial_test::serial; +use signature::{Keypair, Signer, Verifier}; +use testresult::TestResult; + +#[test] +#[serial] +fn sign_verify() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // get mechanism + let mechanism = Mechanism::EccKeyPairGen; + + let secp256r1_oid: Vec = p256::NistP256::OID.to_der().unwrap(); + println!("oid: {:x?}", secp256r1_oid); + + let label = b"demo-signer"; + + // pub key template + let pub_key_template = vec![ + Attribute::Token(false), + Attribute::Private(false), + Attribute::KeyType(KeyType::EC), + Attribute::Verify(true), + Attribute::EcParams(secp256r1_oid.clone()), + Attribute::Label(label.to_vec()), + ]; + + // priv key template + let priv_key_template = vec![ + Attribute::Token(false), + Attribute::Private(true), + Attribute::Sign(true), + Attribute::Label(label.to_vec()), + ]; + + // generate a key pair + let (public, private) = + session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?; + + // data to sign + let data = [0xFF, 0x55, 0xDD]; + + let signer = + ecdsa::Signer::::new(&session, label).expect("Lookup keys from HSM"); + + let signature: p256::ecdsa::Signature = signer.sign(&data); + + let verifying_key = signer.verifying_key(); + verifying_key.verify(&data, &signature)?; + + // delete keys + session.destroy_object(public)?; + session.destroy_object(private)?; + + Ok(()) +} + +#[test] +#[serial] +fn test_import() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + let mut rng = rand::thread_rng(); + let private = p256::ecdsa::SigningKey::random(&mut rng); + + let label = b"demo-import"; + + let template = vec![Attribute::Token(false), Attribute::Label(label.to_vec())]; + + let private_handle = p256::SecretKey::from(&private).put_key(&session, template.clone())?; + let public_handle = + p256::PublicKey::from(private.verifying_key()).put_key(&session, template)?; + + // data to sign + let data = [0xFF, 0x55, 0xDD]; + + let signer = + ecdsa::Signer::::new(&session, label).expect("Lookup keys from HSM"); + + let signature: p256::ecdsa::Signature = signer.sign(&data); + + let verifying_key = private.verifying_key(); + verifying_key.verify(&data, &signature)?; + + // delete keys + session.destroy_object(private_handle)?; + session.destroy_object(public_handle)?; + + Ok(()) +} diff --git a/cryptoki-rustcrypto/tests/rng.rs b/cryptoki-rustcrypto/tests/rng.rs new file mode 100644 index 00000000..38184d6a --- /dev/null +++ b/cryptoki-rustcrypto/tests/rng.rs @@ -0,0 +1,57 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +mod common; + +use crate::common::USER_PIN; +use common::init_pins; +use cryptoki::{session::UserType, types::AuthPin}; +use cryptoki_rustcrypto::rng::Rng; +use serial_test::serial; +use signature::rand_core::{CryptoRngCore, RngCore}; +use testresult::TestResult; + +// This test is meant to ensure we provide [`rand_core::CryptoRngCore`]. +// This is the trait consumers will use throughout the RustCrypto ecosystem +// to express interest in a CSPRNG. +#[test] +#[serial] +fn ensure_crypto_rng_core() -> TestResult { + fn just_making_sure(_r: &mut R) { + // Hi! just making sure you provide a CSPRNG. + } + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + let mut rng = Rng::new(session).unwrap(); + just_making_sure(&mut rng); + + Ok(()) +} + +#[test] +#[serial] +fn generate_random() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + let mut rng = Rng::new(session).unwrap(); + rng.next_u32(); + rng.next_u64(); + + let mut buf = vec![0; 123]; + rng.fill_bytes(&mut buf); + rng.try_fill_bytes(&mut buf).unwrap(); + + Ok(()) +} diff --git a/cryptoki-rustcrypto/tests/rsa.rs b/cryptoki-rustcrypto/tests/rsa.rs new file mode 100644 index 00000000..0ca285b9 --- /dev/null +++ b/cryptoki-rustcrypto/tests/rsa.rs @@ -0,0 +1,175 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +mod common; + +use crate::common::USER_PIN; +use common::init_pins; +use cryptoki::{mechanism::Mechanism, object::Attribute, session::UserType, types::AuthPin}; +use cryptoki_rustcrypto::rsa::{pkcs1v15, pss}; +use rand::{thread_rng, RngCore}; +use serial_test::serial; +use sha2::{Digest, Sha256}; +use signature::{hazmat::PrehashSigner, Keypair, Signer, Verifier}; +use testresult::TestResult; + +#[test] +#[serial] +fn pkcs1v15_sign_verify() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // get mechanism + let mechanism = Mechanism::RsaPkcsKeyPairGen; + + let public_exponent: Vec = vec![0x01, 0x00, 0x01]; + let modulus_bits = 1024; + + let label = b"demo-signer"; + + // pub key template + let pub_key_template = vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Label(label.to_vec()), + Attribute::PublicExponent(public_exponent), + Attribute::ModulusBits(modulus_bits.into()), + ]; + + // priv key template + let priv_key_template = vec![Attribute::Token(true), Attribute::Label(label.to_vec())]; + + // generate a key pair + let (public, private) = + session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?; + + // data to sign + let data = [0xFF, 0x55, 0xDD]; + + let signer = pkcs1v15::Signer::::new(&session, label).expect("Lookup keys from HSM"); + + let signature = signer.sign(&data); + + let verifying_key = signer.verifying_key(); + verifying_key.verify(&data, &signature)?; + + // delete keys + session.destroy_object(public)?; + session.destroy_object(private)?; + + Ok(()) +} + +#[test] +#[serial] +fn pss_sign_verify() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // get mechanism + let mechanism = Mechanism::RsaPkcsKeyPairGen; + + let public_exponent: Vec = vec![0x01, 0x00, 0x01]; + let modulus_bits = 1024; + + let label = b"demo-signer"; + + // pub key template + let pub_key_template = vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Label(label.to_vec()), + Attribute::PublicExponent(public_exponent), + Attribute::ModulusBits(modulus_bits.into()), + ]; + + // priv key template + let priv_key_template = vec![Attribute::Token(true), Attribute::Label(label.to_vec())]; + + // generate a key pair + let (public, private) = + session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?; + + // data to sign + let data = [0xFF, 0x55, 0xDD]; + + let signer = pss::Signer::::new(&session, label).expect("Lookup keys from HSM"); + + let signature = signer.sign(&data); + + let verifying_key = signer.verifying_key(); + verifying_key.verify(&data, &signature)?; + + // delete keys + session.destroy_object(public)?; + session.destroy_object(private)?; + + Ok(()) +} + +#[test] +#[serial] +fn pkcs1v15_sign_verify_prehashed() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // get mechanism + let mechanism = Mechanism::RsaPkcsKeyPairGen; + + let public_exponent: Vec = vec![0x01, 0x00, 0x01]; + let modulus_bits = 1024; + + let label = b"demo-signer"; + + // pub key template + let pub_key_template = vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Label(label.to_vec()), + Attribute::PublicExponent(public_exponent), + Attribute::ModulusBits(modulus_bits.into()), + ]; + + // priv key template + let priv_key_template = vec![Attribute::Token(true), Attribute::Label(label.to_vec())]; + + // generate a key pair + let (public, private) = + session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?; + + // data to sign + let mut data = [0u8; 7123]; + thread_rng().fill_bytes(&mut data[..]); + + let prehashed = Sha256::digest(&data[..]); + + let signer = pkcs1v15::Signer::::new(&session, label).expect("Lookup keys from HSM"); + + let signature1 = signer.sign(&data); + let signature2 = signer.sign_prehash(&prehashed).expect("Sign prehash"); + + let verifying_key = signer.verifying_key(); + verifying_key.verify(&data, &signature1)?; + verifying_key.verify(&data, &signature2)?; + + // delete keys + session.destroy_object(public)?; + session.destroy_object(private)?; + + Ok(()) +} diff --git a/cryptoki-rustcrypto/tests/verisign.der b/cryptoki-rustcrypto/tests/verisign.der new file mode 100644 index 00000000..d94106f8 Binary files /dev/null and b/cryptoki-rustcrypto/tests/verisign.der differ diff --git a/cryptoki-rustcrypto/tests/x509-ca.rs b/cryptoki-rustcrypto/tests/x509-ca.rs new file mode 100644 index 00000000..c6ab5b7b --- /dev/null +++ b/cryptoki-rustcrypto/tests/x509-ca.rs @@ -0,0 +1,154 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +mod common; + +use crate::common::USER_PIN; +use common::init_pins; +use cryptoki::{ + mechanism::Mechanism, + object::{Attribute, KeyType}, + session::UserType, + types::AuthPin, +}; +use cryptoki_rustcrypto::{ecdsa, rsa::pss}; +use der::{pem::LineEnding, Encode, EncodePem}; +use p256::pkcs8::AssociatedOid; +use serial_test::serial; +use signature::Keypair; +use spki::SubjectPublicKeyInfoOwned; +use std::{str::FromStr, time::Duration}; +use testresult::TestResult; +use x509_cert::{ + builder::{profile::cabf, Builder, CertificateBuilder}, + name::Name, + serial_number::SerialNumber, + time::Validity, +}; + +#[test] +#[serial] +fn pss_create_ca() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // get mechanism + let mechanism = Mechanism::RsaPkcsKeyPairGen; + + let public_exponent: Vec = vec![0x01, 0x00, 0x01]; + let modulus_bits = 1024; + + let label = b"demo-signer"; + + // pub key template + let pub_key_template = vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::Label(label.to_vec()), + Attribute::PublicExponent(public_exponent), + Attribute::ModulusBits(modulus_bits.into()), + ]; + + // priv key template + let priv_key_template = vec![Attribute::Token(true), Attribute::Label(label.to_vec())]; + + // generate a key pair + let (public, private) = + session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?; + + let signer = + pss::Signer::::new(&session, label).expect("Lookup keys from HSM"); + + let serial_number = SerialNumber::from(42u32); + let validity = Validity::from_now(Duration::new(5, 0)).unwrap(); + let subject = + Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap(); + let profile = cabf::Root::new(false, subject).expect("Create root profile"); + let pub_key = SubjectPublicKeyInfoOwned::from_key(&signer.verifying_key()).unwrap(); + + let builder = CertificateBuilder::new(profile, serial_number, validity, pub_key) + .expect("Create certificate"); + + let certificate = builder.build(&signer).unwrap(); + + let pem = certificate.to_pem(LineEnding::LF).expect("generate pem"); + println!("{}", pem); + + // delete keys + session.destroy_object(public)?; + session.destroy_object(private)?; + + Ok(()) +} + +#[test] +#[serial] +fn ecdsa_create_ca() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + // get mechanism + let mechanism = Mechanism::EccKeyPairGen; + + let secp256r1_oid: Vec = p256::NistP256::OID.to_der().unwrap(); + + let label = b"demo-signer"; + + // pub key template + let pub_key_template = vec![ + Attribute::Token(true), + Attribute::Private(false), + Attribute::KeyType(KeyType::EC), + Attribute::Verify(true), + Attribute::EcParams(secp256r1_oid.clone()), + Attribute::Label(label.to_vec()), + ]; + + // priv key template + let priv_key_template = vec![ + Attribute::Token(true), + Attribute::Private(true), + Attribute::Sign(true), + Attribute::Label(label.to_vec()), + ]; + + // generate a key pair + let (public, private) = + session.generate_key_pair(&mechanism, &pub_key_template, &priv_key_template)?; + + let signer = + ecdsa::Signer::::new(&session, label).expect("Lookup keys from HSM"); + + let serial_number = SerialNumber::from(42u32); + let validity = Validity::from_now(Duration::new(5, 0)).unwrap(); + let subject = + Name::from_str("CN=World domination corporation,O=World domination Inc,C=US").unwrap(); + let profile = cabf::Root::new(false, subject).expect("create root profile"); + let pub_key = SubjectPublicKeyInfoOwned::from_key(&signer.verifying_key()).unwrap(); + + let builder = CertificateBuilder::new(profile, serial_number, validity, pub_key) + .expect("Create certificate"); + + let certificate = builder + .build::<_, p256::ecdsa::DerSignature>(&signer) + .unwrap(); + + let pem = certificate.to_pem(LineEnding::LF).expect("generate pem"); + println!("{}", pem); + + // delete keys + session.destroy_object(public)?; + session.destroy_object(private)?; + + Ok(()) +} diff --git a/cryptoki-rustcrypto/tests/x509.rs b/cryptoki-rustcrypto/tests/x509.rs new file mode 100644 index 00000000..b0842ad8 --- /dev/null +++ b/cryptoki-rustcrypto/tests/x509.rs @@ -0,0 +1,41 @@ +// Copyright 2023 Contributors to the Parsec project. +// SPDX-License-Identifier: Apache-2.0 + +mod common; + +use crate::common::USER_PIN; +use common::init_pins; +use cryptoki::{object::Attribute, session::UserType, types::AuthPin}; +use cryptoki_rustcrypto::x509::CertPkcs11; +use der::Decode; +use serial_test::serial; +use testresult::TestResult; +use x509_cert::Certificate; + +const VERISIGN_CERT: &[u8] = include_bytes!("./verisign.der"); + +#[test] +#[serial] +fn test_x509() -> TestResult { + let (pkcs11, slot) = init_pins(); + + // open a session + let session = pkcs11.open_rw_session(slot)?; + + // log in the session + session.login(UserType::User, Some(&AuthPin::new(USER_PIN.into())))?; + + let cert = Certificate::from_der(VERISIGN_CERT).expect("read certificate from der"); + + let base_template = vec![Attribute::Label(b"demo-cert".to_vec())]; + + cert.pkcs11_store(&session, base_template.clone()) + .expect("Store cert with the PKCS11 provider"); + + let new = Certificate::pkcs11_load(&session, base_template) + .expect("Lookup cert from PKCS11 provider"); + + assert_eq!(cert, new); + + Ok(()) +}