diff --git a/Cargo.lock b/Cargo.lock index 286d572bf63af1..a24321be7dd843 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1151,6 +1151,20 @@ dependencies = [ "tracing", ] +[[package]] +name = "aws-nitro-enclaves-cose" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a94047bd9c3717c6ca3a145504c0e26b64a5e2d9eb9559b187748433fbc382" +dependencies = [ + "openssl", + "serde", + "serde_bytes", + "serde_cbor", + "serde_repr", + "serde_with 3.9.0", +] + [[package]] name = "aws-runtime" version = "0.56.1" @@ -5052,6 +5066,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -9103,12 +9132,50 @@ dependencies = [ "serde_json", ] +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags 2.4.1", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2 1.0.87", + "quote 1.0.35", + "syn 2.0.79", +] + [[package]] name = "openssl-probe" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "opentelemetry" version = "0.21.0" @@ -11803,6 +11870,16 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half 1.8.2", + "serde", +] + [[package]] name = "serde_derive" version = "1.0.210" @@ -14291,6 +14368,7 @@ dependencies = [ name = "sui-move-natives-v2" version = "0.1.0" dependencies = [ + "aws-nitro-enclaves-cose", "bcs", "better_any", "fastcrypto", @@ -14301,6 +14379,10 @@ dependencies = [ "move-stdlib-natives-v2", "move-vm-runtime-v2", "move-vm-types", + "openssl", + "rustls 0.20.9", + "serde", + "serde_cbor", "smallvec", "sui-protocol-config", "sui-types", diff --git a/crates/sui-framework/packages/sui-framework/sources/crypto/attestation.move b/crates/sui-framework/packages/sui-framework/sources/crypto/attestation.move new file mode 100644 index 00000000000000..7f2bf2d27c82f6 --- /dev/null +++ b/crates/sui-framework/packages/sui-framework/sources/crypto/attestation.move @@ -0,0 +1,19 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +module sui::attestation; + +/// @param attestation: attesttaion documents bytes data. +/// @param enclave_pk: public key from enclave +/// +/// If the signature is a valid Ed25519 signature of the message and public key, return true. +/// Otherwise, return false. +public native fun attestation_verify( + enclave_pk: &vector, + attestation: &vector, +): bool; + +public native fun tpm2_attestation_verify( + user_data: &vector, + attestation: &vector, +): bool; diff --git a/crates/sui-framework/packages/sui-framework/tests/crypto/attestation_tests.move b/crates/sui-framework/packages/sui-framework/tests/crypto/attestation_tests.move new file mode 100644 index 00000000000000..058cb1106410aa --- /dev/null +++ b/crates/sui-framework/packages/sui-framework/tests/crypto/attestation_tests.move @@ -0,0 +1,29 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +#[allow(implicit_const_copy)] +#[test_only] +module sui::bls12381_tests { + use sui::bls12381; + use sui::group_ops; + use sui::random; + use sui::test_utils::assert_eq; + use std::hash::sha2_256; + + const ORDER_BYTES: vector = x"73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001"; + const ORDER_MINUS_ONE_BYTES: vector = x"73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000000"; + const LONG_SCALAR_BYTES: vector = x"73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff0000000000"; + const SHORT_SCALAR_BYTES: vector = x"73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff0000"; + const LONG_G1_BYTES: vector = x"97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bbbbbb"; + const SHORT_G1_BYTES: vector = x"97f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb"; + const LONG_G2_BYTES: vector = x"93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb811"; + const SHORT_G2_BYTES: vector = x"93e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e024aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121"; + + #[test] + fun test_attestation_verify() { + let attestation = x"8444a1013822a0591120a9696d6f64756c655f69647827692d30326130666132393836396564363165642d656e633031393262663561663336613733613566646967657374665348413338346974696d657374616d701b00000192bff6ad796470637273b0005830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025830000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035830639a8b65f68b0223cbb14a0032487e5656d260434e3d1a10e7ec1407fb86143860717fc8afee90df7a1604111709af460458300c5c8067bd308b422bbe9654cbb0b3d13a49a5470d04865497582661f1910f7bd2cf840560d302391ca4c66f46c258520558300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000658300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000758300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000858300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000958300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f58300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006b636572746966696361746559027f3082027b30820201a00302010202100192bf5af36a73a500000000671a9d5b300a06082a8648ce3d04030330818e310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3110300e06035504070c0753656174746c65310f300d060355040a0c06416d617a6f6e310c300a060355040b0c034157533139303706035504030c30692d30326130666132393836396564363165642e75732d656173742d312e6177732e6e6974726f2d656e636c61766573301e170d3234313032343139313734345a170d3234313032343232313734375a308193310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3110300e06035504070c0753656174746c65310f300d060355040a0c06416d617a6f6e310c300a060355040b0c03415753313e303c06035504030c35692d30326130666132393836396564363165642d656e63303139326266356166333661373361352e75732d656173742d312e6177733076301006072a8648ce3d020106052b810400220362000419e33204ae5676d5706f428e99aeaf3c97a5c6d8bd6ff2c8f7c221d1d79303e3ed9408de07a33060060ae4f62631a10d2fb2c6f770c679ea895ba298128811f28e7a3a811d2edd9b2d891239b43effd1a475c8e377ef88d2e3d244086874a245a31d301b300c0603551d130101ff04023000300b0603551d0f0404030206c0300a06082a8648ce3d0403030368003065023100a97e905326fc6b9556254fbf1226d6719989c21b87ac735961a6a5e0a56fb667b232c9937423169b02ea8dbbf097aeb40230499f8e4c09856e2246a2adab33adfbe490968fb916b599a992e2e20594a0f7118e3d9e18c6e7a900fc4a113752fe310868636162756e646c65845902153082021130820196a003020102021100f93175681b90afe11d46ccb4e4e7f856300a06082a8648ce3d0403033049310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c03415753311b301906035504030c126177732e6e6974726f2d656e636c61766573301e170d3139313032383133323830355a170d3439313032383134323830355a3049310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c03415753311b301906035504030c126177732e6e6974726f2d656e636c617665733076301006072a8648ce3d020106052b8104002203620004fc0254eba608c1f36870e29ada90be46383292736e894bfff672d989444b5051e534a4b1f6dbe3c0bc581a32b7b176070ede12d69a3fea211b66e752cf7dd1dd095f6f1370f4170843d9dc100121e4cf63012809664487c9796284304dc53ff4a3423040300f0603551d130101ff040530030101ff301d0603551d0e041604149025b50dd90547e796c396fa729dcf99a9df4b96300e0603551d0f0101ff040403020186300a06082a8648ce3d0403030369003066023100a37f2f91a1c9bd5ee7b8627c1698d255038e1f0343f95b63a9628c3d39809545a11ebcbf2e3b55d8aeee71b4c3d6adf3023100a2f39b1605b27028a5dd4ba069b5016e65b4fbde8fe0061d6a53197f9cdaf5d943bc61fc2beb03cb6fee8d2302f3dff65902c2308202be30820244a00302010202101cd0c4a846859575fe3ecdb330e852a0300a06082a8648ce3d0403033049310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c03415753311b301906035504030c126177732e6e6974726f2d656e636c61766573301e170d3234313032343039333235355a170d3234313131333130333235355a3064310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c034157533136303406035504030c2d613132616566333232643530353436352e75732d656173742d312e6177732e6e6974726f2d656e636c617665733076301006072a8648ce3d020106052b8104002203620004d09c64a02b070e9ab2f4ed69d2c2592e52499fd32cc10de3d348b861a2463f095bd78072b248add25aebb092fcd483db4dac438410ce16fa7a4e3289f47a30b29dc15dc5767a5bd9fec5fe4db8876de017b0e3766b70fc7915c57f9a8b183bb0a381d53081d230120603551d130101ff040830060101ff020102301f0603551d230418301680149025b50dd90547e796c396fa729dcf99a9df4b96301d0603551d0e041604146ccdaf765a77d0d75639755b06f339a61aec9431300e0603551d0f0101ff040403020186306c0603551d1f046530633061a05fa05d865b687474703a2f2f6177732d6e6974726f2d656e636c617665732d63726c2e73332e616d617a6f6e6177732e636f6d2f63726c2f61623439363063632d376436332d343262642d396539662d3539333338636236376638342e63726c300a06082a8648ce3d04030303680030650230784f6435c8c2db44be0d5e3c2ef4b975c2de65f3d8b26193628d38173e7f902729e5908ad8c6cbbbee969c6bd712c3b4023100ac0a805da8838a344a053bb6aea203a7c44ab916080c8e6bd91c98cd057918fe368d53105b2b6d25acce71ac6b97895b59031a308203163082029ba003020102021100d3f7e82c3fc7dbbd7ac57ebb3e8a4fea300a06082a8648ce3d0403033064310b3009060355040613025553310f300d060355040a0c06416d617a6f6e310c300a060355040b0c034157533136303406035504030c2d613132616566333232643530353436352e75732d656173742d312e6177732e6e6974726f2d656e636c61766573301e170d3234313032343130313230335a170d3234313033303034313230335a308189313c303a06035504030c33356637313231383234373530333739362e7a6f6e616c2e75732d656173742d312e6177732e6e6974726f2d656e636c61766573310c300a060355040b0c03415753310f300d060355040a0c06416d617a6f6e310b3009060355040613025553310b300906035504080c0257413110300e06035504070c0753656174746c653076301006072a8648ce3d020106052b810400220362000424160078a3019a9cfc024525a4133aeb46c39a97bbc0811a7df74fb11a28418b74bcbeaf85ad52811c4844c7427bc0377872c2caa6f582fb20f84a5f84bbf0b4efde64646a9fbd97d60a31c8e94f398c22a33cddcb77c59c16f6bf302bdfb091a381ea3081e730120603551d130101ff040830060101ff020101301f0603551d230418301680146ccdaf765a77d0d75639755b06f339a61aec9431301d0603551d0e04160414c670ada693df3d381aebc1c7fe214f4e0ed95163300e0603551d0f0101ff0404030201863081800603551d1f047930773075a073a071866f687474703a2f2f63726c2d75732d656173742d312d6177732d6e6974726f2d656e636c617665732e73332e75732d656173742d312e616d617a6f6e6177732e636f6d2f63726c2f63303838663561312d653531322d346663342d626135622d6561373931316137376636652e63726c300a06082a8648ce3d0403030369003066023100c5b57f998e0225974e291a6ffee3d7c93122c778de01770cfdd92cae6648e58559c5a582d5a388737da2d77f69f7cf1002310085088eb257f6f5db83348820fc1f41597c53762f387abf49ff852b44dfa188883b20d66b7afa5a2a17fa7351d649153f5902c1308202bd30820244a00302010202144500f9709fede9b4b43789ef88bede7efb2b1e6d300a06082a8648ce3d040303308189313c303a06035504030c33356637313231383234373530333739362e7a6f6e616c2e75732d656173742d312e6177732e6e6974726f2d656e636c61766573310c300a060355040b0c03415753310f300d060355040a0c06416d617a6f6e310b3009060355040613025553310b300906035504080c0257413110300e06035504070c0753656174746c65301e170d3234313032343139303734345a170d3234313032353139303734345a30818e310b30090603550406130255533113301106035504080c0a57617368696e67746f6e3110300e06035504070c0753656174746c65310f300d060355040a0c06416d617a6f6e310c300a060355040b0c034157533139303706035504030c30692d30326130666132393836396564363165642e75732d656173742d312e6177732e6e6974726f2d656e636c617665733076301006072a8648ce3d020106052b81040022036200044dae149b171de0872413260f133ce391a397c254dbf0c7c25f0dc0a8a28fa6d2354c9acdc66c74e88389bfed97d33a77fcf77973a8f1381c5d65aa026eb571027844c486e91c18d7bba97907336a814b782f1bc56d722a9fbdd0616589cdb43aa366306430120603551d130101ff040830060101ff020100300e0603551d0f0101ff040403020204301d0603551d0e041604140e872a9e434237fa56cab905f466bbea99848abd301f0603551d23041830168014c670ada693df3d381aebc1c7fe214f4e0ed95163300a06082a8648ce3d040303036700306402304b324ab3d2f1edec5c6715f548f816a4229caa642d067188693413780fdda3194bc469c4f4e421038500b437dc0aeba30230672cca9937e8f711b170a8fc59790bc8e2de5e61cca7011b2d00fde88dd28d6521a4f10fe7b813eaccaf72e4dc6cadfa6a7075626c69635f6b6579f669757365725f646174615820af3ef9c81f191839c060785ce2db7ffb9aaaabb314ffc6a6db774fb9453ef3a2656e6f6e6365f65860fdfe27bd43732ba44c870a48502da367f528291a5058ba95461669468f9d48cf6f07590a5408d4908ab4a84c9b37209d0bd60c52523132da478b0e8a67768316310056ae2e8b39a600ee991bd37bc22ebf06f180e264c86c27471e22eb535652"; + let enclave_pk = x"af3ef9c81f191839c060785ce2db7ffb9aaaabb314ffc6a6db774fb9453ef3a2"; + let verify = attestation::attestation_verify(&enclave_pk, &attestation); + assert!(verify == false) + } +} diff --git a/sui-execution/v2/sui-move-natives/Cargo.toml b/sui-execution/v2/sui-move-natives/Cargo.toml index c2b3c44e5fb8b2..27175295575a89 100644 --- a/sui-execution/v2/sui-move-natives/Cargo.toml +++ b/sui-execution/v2/sui-move-natives/Cargo.toml @@ -25,3 +25,8 @@ move-vm-runtime = { path = "../../../external-crates/move/move-execution/v2/crat sui-protocol-config.workspace = true sui-types.workspace = true tracing.workspace = true +openssl = "0.10.31" +serde.workspace = true +rustls = { version = "0.20.4", features = [ "dangerous_configuration" ] } +aws-nitro-enclaves-cose = "0.5.2" +serde_cbor = {version = "0.11" } diff --git a/sui-execution/v2/sui-move-natives/src/crypto/attestation.rs b/sui-execution/v2/sui-move-natives/src/crypto/attestation.rs new file mode 100644 index 00000000000000..f265bd28d1e591 --- /dev/null +++ b/sui-execution/v2/sui-move-natives/src/crypto/attestation.rs @@ -0,0 +1,368 @@ +// Copyright (c) Mysten Labs, Inc. +// SPDX-License-Identifier: Apache-2.0 + +use move_binary_format::errors::PartialVMResult; +use move_core_types::gas_algebra::InternalGas; +use move_vm_runtime::native_functions::NativeContext; +use move_vm_types::{ + loaded_data::runtime_types::Type, + natives::function::NativeResult, + pop_arg, + values::{Value, VectorRef}, +}; +use smallvec::smallvec; +use std::collections::VecDeque; +// todo: remove this dep +use aws_nitro_enclaves_cose::crypto::{Hash, MessageDigest}; +use aws_nitro_enclaves_cose::error::CoseError; +use openssl::x509::X509; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +pub fn aws_attestation_verify( + context: &mut NativeContext, + ty_args: Vec, + mut args: VecDeque, +) -> PartialVMResult { + debug_assert!(ty_args.is_empty()); + debug_assert!(args.len() == 2); + + // todo: figure out cost + + let attestation = pop_arg!(args, VectorRef); + let user_data = pop_arg!(args, VectorRef); + + let attestation_ref = attestation.as_bytes_ref(); + let user_data_ref = user_data.as_bytes_ref(); + + let pem_data = r"-----BEGIN CERTIFICATE----- +MIICETCCAZagAwIBAgIRAPkxdWgbkK/hHUbMtOTn+FYwCgYIKoZIzj0EAwMwSTEL +MAkGA1UEBhMCVVMxDzANBgNVBAoMBkFtYXpvbjEMMAoGA1UECwwDQVdTMRswGQYD +VQQDDBJhd3Mubml0cm8tZW5jbGF2ZXMwHhcNMTkxMDI4MTMyODA1WhcNNDkxMDI4 +MTQyODA1WjBJMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGQW1hem9uMQwwCgYDVQQL +DANBV1MxGzAZBgNVBAMMEmF3cy5uaXRyby1lbmNsYXZlczB2MBAGByqGSM49AgEG +BSuBBAAiA2IABPwCVOumCMHzaHDimtqQvkY4MpJzbolL//Zy2YlES1BR5TSksfbb +48C8WBoyt7F2Bw7eEtaaP+ohG2bnUs990d0JX28TcPQXCEPZ3BABIeTPYwEoCWZE +h8l5YoQwTcU/9KNCMEAwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUkCW1DdkF +R+eWw5b6cp3PmanfS5YwDgYDVR0PAQH/BAQDAgGGMAoGCCqGSM49BAMDA2kAMGYC +MQCjfy+Rocm9Xue4YnwWmNJVA44fA0P5W2OpYow9OYCVRaEevL8uO1XYru5xtMPW +rfMCMQCi85sWBbJwKKXdS6BptQFuZbT73o/gBh1qUxl/nNr12UO8Yfwr6wPLb+6N +IwLz3/Y= +-----END CERTIFICATE-----"; + let x509 = X509::from_pem(pem_data.as_bytes()).unwrap(); + let trusted_root_cert = x509.to_der().unwrap(); + + let (_protected, payload, _signature) = AttestationDocument::parse(&attestation_ref) + .map_err(|err| format!("AttestationDocument::authenticate parse failed:{:?}", err)) + .unwrap(); + + let document = AttestationDocument::parse_payload(&payload) + .map_err(|err| format!("AttestationDocument::authenticate failed:{:?}", err)) + .unwrap(); + if document.clone().user_data.unwrap() != user_data_ref.to_vec() { + return Ok(NativeResult::ok( + InternalGas::zero(), + smallvec![Value::bool(false)], + )); + } + if AttestationDocument::authenticate(&document, &attestation_ref, &trusted_root_cert).is_err() { + Ok(NativeResult::ok( + InternalGas::zero(), + smallvec![Value::bool(false)], + )) + } else { + Ok(NativeResult::ok( + InternalGas::zero(), + smallvec![Value::bool(true)], + )) + } +} + +/// Type that implements various cryptographic traits using the OpenSSL library +pub struct Openssl; + +impl Hash for Openssl { + fn hash(digest: MessageDigest, data: &[u8]) -> Result, CoseError> { + openssl::hash::hash(digest.into(), data) + .map_err(|e| CoseError::HashingError(Box::new(e))) + .map(|h| h.to_vec()) + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct AttestationDocument { + pub module_id: String, + pub timestamp: u64, + pub digest: String, + pub pcrs: Vec>, + pub certificate: Vec, + pub cabundle: Vec>, + pub public_key: Option>, + pub user_data: Option>, + pub nonce: Option>, +} + +impl AttestationDocument { + pub fn authenticate( + document: &AttestationDocument, + document_data: &[u8], + trusted_root_cert: &[u8], + ) -> Result<(), String> { + // Following the steps here: https://docs.aws.amazon.com/enclaves/latest/user/verify-root.html + // Step 1. Decode the CBOR object and map it to a COSE_Sign1 structure + // Step 2. Exract the attestation document from the COSE_Sign1 structure + // Step 3. Verify the certificate's chain + let mut certs: Vec = Vec::new(); + for this_cert in document.cabundle.clone().iter().rev() { + let cert = rustls::Certificate(this_cert.to_vec()); + certs.push(cert); + } + let cert = rustls::Certificate(document.certificate.clone()); + certs.push(cert); + + let mut root_store = rustls::RootCertStore::empty(); + root_store + .add(&rustls::Certificate(trusted_root_cert.to_vec())) + .map_err(|err| { + format!( + "AttestationDocument::authenticate failed to add trusted root cert:{:?}", + err + ) + })?; + + let verifier = rustls::server::AllowAnyAuthenticatedClient::new(root_store); + let _verified = verifier + .verify_client_cert( + &rustls::Certificate(document.certificate.clone()), + &certs, + std::time::SystemTime::now(), + ) + .map_err(|err| { + format!( + "AttestationDocument::authenticate verify_client_cert failed:{:?}", + err + ) + })?; + // if verify_client_cert didn't generate an error, authentication passed + + // Step 4. Ensure the attestation document is properly signed + let authenticated = { + let sig_structure = aws_nitro_enclaves_cose::sign::CoseSign1::from_bytes(document_data) + .map_err(|err| { + format!("AttestationDocument::authenticate failed to load document_data as COSESign1 structure:{:?}", err) + })?; + let cert = openssl::x509::X509::from_der(&document.certificate) + .map_err(|err| { + format!("AttestationDocument::authenticate failed to parse document.certificate as X509 certificate:{:?}", err) + })?; + let public_key = cert.public_key() + .map_err(|err| { + format!("AttestationDocument::authenticate failed to extract public key from certificate:{:?}", err) + })?; + + sig_structure.verify_signature::(&public_key) + .map_err(|err| { + format!("AttestationDocument::authenticate failed to verify signature on sig_structure:{:?}", err) + })? + }; + if !authenticated { + Err(format!( + "AttestationDocument::authenticate invalid COSE certificate for provided key" + )) + } else { + Ok(()) + } + } + + pub fn parse(document_data: &[u8]) -> Result<(Vec, Vec, Vec), String> { + let cbor: serde_cbor::Value = serde_cbor::from_slice(document_data) + .map_err(|err| format!("AttestationDocument::parse from_slice failed:{:?}", err))?; + let elements = match cbor { + serde_cbor::Value::Array(elements) => elements, + _ => panic!("AttestationDocument::parse Unknown field cbor:{:?}", cbor), + }; + let protected = match &elements[0] { + serde_cbor::Value::Bytes(prot) => prot, + _ => panic!( + "AttestationDocument::parse Unknown field protected:{:?}", + elements[0] + ), + }; + let _unprotected = match &elements[1] { + serde_cbor::Value::Map(unprot) => unprot, + _ => panic!( + "AttestationDocument::parse Unknown field unprotected:{:?}", + elements[1] + ), + }; + let payload = match &elements[2] { + serde_cbor::Value::Bytes(payld) => payld, + _ => panic!( + "AttestationDocument::parse Unknown field payload:{:?}", + elements[2] + ), + }; + let signature = match &elements[3] { + serde_cbor::Value::Bytes(sig) => sig, + _ => panic!( + "AttestationDocument::parse Unknown field signature:{:?}", + elements[3] + ), + }; + Ok((protected.to_vec(), payload.to_vec(), signature.to_vec())) + } + + pub fn parse_payload(payload: &Vec) -> Result { + let document_data: serde_cbor::Value = serde_cbor::from_slice(payload.as_slice()) + .map_err(|err| format!("document parse failed:{:?}", err))?; + + let document_map: BTreeMap = match document_data { + serde_cbor::Value::Map(map) => map, + _ => { + return Err(format!( + "AttestationDocument::parse_payload field ain't what it should be:{:?}", + document_data + )) + } + }; + + let module_id: String = + match document_map.get(&serde_cbor::Value::Text("module_id".to_string())) { + Some(serde_cbor::Value::Text(val)) => val.to_string(), + _ => { + return Err( + "AttestationDocument::parse_payload module_id is wrong type or not present" + .to_string(), + ) + } + }; + + let timestamp: i128 = + match document_map.get(&serde_cbor::Value::Text("timestamp".to_string())) { + Some(serde_cbor::Value::Integer(val)) => *val, + _ => { + return Err( + "AttestationDocument::parse_payload timestamp is wrong type or not present" + .to_string(), + ) + } + }; + + let timestamp: u64 = timestamp.try_into().map_err(|err| { + format!( + "AttestationDocument::parse_payload failed to convert timestamp to u64:{:?}", + err + ) + })?; + + let public_key: Option> = + match document_map.get(&serde_cbor::Value::Text("public_key".to_string())) { + Some(serde_cbor::Value::Bytes(val)) => Some(val.to_vec()), + Some(_null) => None, + None => None, + }; + + let certificate: Vec = + match document_map.get(&serde_cbor::Value::Text("certificate".to_string())) { + Some(serde_cbor::Value::Bytes(val)) => val.to_vec(), + _ => return Err( + "AttestationDocument::parse_payload certificate is wrong type or not present" + .to_string(), + ), + }; + + let pcrs: Vec> = match document_map + .get(&serde_cbor::Value::Text("pcrs".to_string())) + { + Some(serde_cbor::Value::Map(map)) => { + let mut ret_vec: Vec> = Vec::new(); + let num_entries:i128 = map.len().try_into() + .map_err(|err| format!("AttestationDocument::parse_payload failed to convert pcrs len into i128:{:?}", err))?; + for x in 0..num_entries { + match map.get(&serde_cbor::Value::Integer(x)) { + Some(serde_cbor::Value::Bytes(inner_vec)) => { + ret_vec.push(inner_vec.to_vec()); + }, + _ => return Err("AttestationDocument::parse_payload pcrs inner vec is wrong type or not there?".to_string()), + } + } + ret_vec + } + _ => { + return Err( + "AttestationDocument::parse_payload pcrs is wrong type or not present" + .to_string(), + ) + } + }; + + let nonce: Option> = + match document_map.get(&serde_cbor::Value::Text("nonce".to_string())) { + Some(serde_cbor::Value::Bytes(val)) => Some(val.to_vec()), + None => None, + _ => { + return Err( + "AttestationDocument::parse_payload nonce is wrong type or not present" + .to_string(), + ) + } + }; + + let user_data: Option> = + match document_map.get(&serde_cbor::Value::Text("user_data".to_string())) { + Some(serde_cbor::Value::Bytes(val)) => Some(val.to_vec()), + None => None, + Some(_null) => None, + }; + + let digest: String = match document_map.get(&serde_cbor::Value::Text("digest".to_string())) + { + Some(serde_cbor::Value::Text(val)) => val.to_string(), + _ => { + return Err( + "AttestationDocument::parse_payload digest is wrong type or not present" + .to_string(), + ) + } + }; + + let cabundle: Vec> = + match document_map.get(&serde_cbor::Value::Text("cabundle".to_string())) { + Some(serde_cbor::Value::Array(outer_vec)) => { + let mut ret_vec: Vec> = Vec::new(); + for this_vec in outer_vec.iter() { + match this_vec { + serde_cbor::Value::Bytes(inner_vec) => { + ret_vec.push(inner_vec.to_vec()); + } + _ => { + return Err( + "AttestationDocument::parse_payload inner_vec is wrong type" + .to_string(), + ) + } + } + } + ret_vec + } + _ => { + return Err(format!( + "AttestationDocument::parse_payload cabundle is wrong type or not present:{:?}", + document_map.get(&serde_cbor::Value::Text("cabundle".to_string())) + )) + } + }; + + Ok(AttestationDocument { + module_id, + timestamp, + digest, + pcrs, + certificate, + cabundle, + public_key, + user_data, + nonce, + }) + } +} diff --git a/sui-execution/v2/sui-move-natives/src/crypto/mod.rs b/sui-execution/v2/sui-move-natives/src/crypto/mod.rs index d52776c94d45a4..94f1f25a7ff99c 100644 --- a/sui-execution/v2/sui-move-natives/src/crypto/mod.rs +++ b/sui-execution/v2/sui-move-natives/src/crypto/mod.rs @@ -1,6 +1,7 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 +pub mod attestation; pub mod bls12381; pub mod ecdsa_k1; pub mod ecdsa_r1; diff --git a/sui-execution/v2/sui-move-natives/src/lib.rs b/sui-execution/v2/sui-move-natives/src/lib.rs index d4147e52d11d2b..9774c95dbf5bbe 100644 --- a/sui-execution/v2/sui-move-natives/src/lib.rs +++ b/sui-execution/v2/sui-move-natives/src/lib.rs @@ -40,6 +40,7 @@ use crate::crypto::poseidon::PoseidonBN254CostParams; use crate::crypto::zklogin; use crate::crypto::zklogin::{CheckZkloginIdCostParams, CheckZkloginIssuerCostParams}; use better_any::{Tid, TidAble}; +use crypto::attestation; use move_binary_format::errors::{PartialVMError, PartialVMResult}; use move_core_types::{ annotated_value as A, @@ -861,6 +862,11 @@ pub fn all_natives(silent: bool) -> NativeFunctionTable { "poseidon_bn254_internal", make_native!(poseidon::poseidon_bn254_internal), ), + ( + "attestation", + "aws_attestation_verify", + make_native!(attestation::aws_attestation_verify), + ), ]; let sui_framework_natives_iter = sui_framework_natives