Skip to content

Commit

Permalink
BLS12-381 host functions (#1456)
Browse files Browse the repository at this point in the history
### What

This PR adds support for BLS12-381 in Soroban, by adding host functions
for the field and curve operations, new metering cost types, and their
calibration.

### Why

Please refer to the
[CAP](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0059.md)
for motivation and design rationale.

### Known limitations
The unit tests currently covers the low-level operations: G1/G2
operations, hashing to curve, pairing.
More test coverage will be added before the PR merged, at the
integration level such as BLS signature and the Ethereum test vectors.

---------

Co-authored-by: Plamen Hristov <[email protected]>
  • Loading branch information
jayz22 and PlamenHristov authored Sep 18, 2024
1 parent cf8023a commit 0497816
Show file tree
Hide file tree
Showing 132 changed files with 8,148 additions and 132 deletions.
226 changes: 197 additions & 29 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ wasmparser = "=0.116.1"
[workspace.dependencies.stellar-xdr]
version = "=22.0.0"
git = "https://github.com/stellar/rs-stellar-xdr"
rev = "39d7dbb0c12bd422ee43a6e2e3277789da4eaac8"
rev = "b5516843b6379e4e29520bf2ba156484f62edc46"
default-features = false

[workspace.dependencies.wasmi]
Expand Down
36 changes: 36 additions & 0 deletions cackle.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ include = [
[api.hash]
include = [
"core::hash",
"derivative::hash",
"std::collections::HashMap",
"std::collections::hash",
"std::sys::unix::rand::hashmap_random_keys",
Expand Down Expand Up @@ -400,6 +401,10 @@ allow_apis = [
"hash",
"thread",
]
build.allow_apis = [
"env",
"process",
]

[pkg.soroban-wasmi]
allow_unsafe = true
Expand Down Expand Up @@ -499,3 +504,34 @@ allow_unsafe = true

[pkg.arrayvec]
allow_unsafe = true

[pkg.ark-serialize-derive]
allow_proc_macro = true

[pkg.zeroize_derive]
allow_proc_macro = true

[pkg.ark-ff-macros]
allow_proc_macro = true

[pkg.derivative]
allow_proc_macro = true
allow_apis = [
"hash",
]

[pkg.ark-ff-asm]
allow_proc_macro = true

[pkg.num-bigint]
build.allow_apis = [
"env",
"fs",
]
allow_unsafe = true

[pkg.data-encoding]
allow_unsafe = true

[pkg.ark-ff]
allow_unsafe = true
2 changes: 1 addition & 1 deletion soroban-builtin-sdk-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ proc-macro = true
syn = {version="2.0.39",features=["full"]}
quote = "1.0.33"
proc-macro2 = "1.0.69"
itertools = "0.11.0"
itertools = "0.10.5"

[package.metadata.docs.rs]
all-features = true
286 changes: 286 additions & 0 deletions soroban-env-common/env.json
Original file line number Diff line number Diff line change
Expand Up @@ -2047,6 +2047,292 @@
"return": "Void",
"docs": "Verifies the `signature` using an ECDSA secp256r1 `public_key` on a 32-byte `msg_digest`. Warning: The `msg_digest` must be produced by a secure cryptographic hash function on the message, otherwise the attacker can potentially forge signatures. The `public_key` is expected to be 65 bytes in length, representing a SEC-1 encoded point in uncompressed format. The `signature` is the ECDSA signature `(r, s)` serialized as fixed-size big endian scalar values, both `r`, `s` must be non-zero and `s` must be in the lower range. ",
"min_supported_protocol": 21
},
{
"export": "4",
"name": "bls12_381_check_g1_is_in_subgroup",
"args": [
{
"name": "point",
"type": "BytesObject"
}
],
"return": "Bool",
"docs": "Checks if the input G1 point is in the correct subgroup.",
"min_supported_protocol": 22
},
{
"export": "5",
"name": "bls12_381_g1_add",
"args": [
{
"name": "point1",
"type": "BytesObject"
},
{
"name": "point2",
"type": "BytesObject"
}
],
"return": "BytesObject",
"docs": "Adds two BLS12-381 G1 points given in bytes format and returns the resulting G1 point in bytes format. G1 serialization format: `concat(be_bytes(X), be_bytes(Y))` and the most significant three bits of X encodes flags, i.e. bits(X) = [compression_flag, infinity_flag, sort_flag, bit_3, .. bit_383]. This function does NOT perform subgroup check on the inputs.",
"min_supported_protocol": 22
},
{
"export": "6",
"name": "bls12_381_g1_mul",
"args": [
{
"name": "point",
"type": "BytesObject"
},
{
"name": "scalar",
"type": "U256Val"
}
],
"return": "BytesObject",
"docs": "Multiplies a BLS12-381 G1 point by a scalar (Fr), and returns the resulting G1 point in bytes format.",
"min_supported_protocol": 22
},
{
"export": "7",
"name": "bls12_381_g1_msm",
"args": [
{
"name": "vp",
"type": "VecObject"
},
{
"name": "vs",
"type": "VecObject"
}
],
"return": "BytesObject",
"docs": "Performs multi-scalar-multiplication (inner product) on a vector of BLS12-381 G1 points (`Vec<BytesObject>`) by a vector of scalars (`Vec<U256Val>`), and returns the resulting G1 point in bytes format.",
"min_supported_protocol": 22
},
{
"export": "8",
"name": "bls12_381_map_fp_to_g1",
"args": [
{
"name": "fp",
"type": "BytesObject"
}
],
"return": "BytesObject",
"docs": "Maps a BLS12-381 field element (Fp) to G1 point. The input is a BytesObject containing Fp serialized in big-endian order",
"min_supported_protocol": 22
},
{
"export": "9",
"name": "bls12_381_hash_to_g1",
"args": [
{
"name": "msg",
"type": "BytesObject"
},
{
"name": "dst",
"type": "BytesObject"
}
],
"return": "BytesObject",
"docs": "Hashes a message to a BLS12-381 G1 point, with implementation following the specification in [Hashing to Elliptic Curves](https://datatracker.ietf.org/doc/html/rfc9380) (ciphersuite 'BLS12381G1_XMD:SHA-256_SSWU_RO_'). `dst` is the domain separation tag that will be concatenated with the `msg` during hashing, it is intended to keep hashing inputs of different applications separate. It is required `0 < len(dst_bytes) < 256`. DST **must** be chosen with care to avoid compromising the application's security properties. Refer to section 3.1 in the RFC on requirements of DST.",
"min_supported_protocol": 22
},
{
"export": "a",
"name": "bls12_381_check_g2_is_in_subgroup",
"args": [
{
"name": "point",
"type": "BytesObject"
}
],
"return": "Bool",
"docs": "Checks if the input G2 point is in the correct subgroup.",
"min_supported_protocol": 22
},
{
"export": "b",
"name": "bls12_381_g2_add",
"args": [
{
"name": "point1",
"type": "BytesObject"
},
{
"name": "point2",
"type": "BytesObject"
}
],
"return": "BytesObject",
"docs": "Adds two BLS12-381 G2 points given in bytes format and returns the resulting G2 point in bytes format. G2 serialization format: concat(be_bytes(X_c1), be_bytes(X_c0), be_bytes(Y_c1), be_bytes(Y_c0)), and the most significant three bits of X_c1 are flags i.e. bits(X_c1) = [compression_flag, infinity_flag, sort_flag, bit_3, .. bit_383]. This function does NOT perform subgroup check on the inputs.",
"min_supported_protocol": 22
},
{
"export": "c",
"name": "bls12_381_g2_mul",
"args": [
{
"name": "point",
"type": "BytesObject"
},
{
"name": "scalar",
"type": "U256Val"
}
],
"return": "BytesObject",
"docs": "Multiplies a BLS12-381 G2 point by a scalar (Fr), and returns the resulting G2 point in bytes format.",
"min_supported_protocol": 22
},
{
"export": "d",
"name": "bls12_381_g2_msm",
"args": [
{
"name": "vp",
"type": "VecObject"
},
{
"name": "vs",
"type": "VecObject"
}
],
"return": "BytesObject",
"docs": "Performs multi-scalar-multiplication (inner product) on a vector of BLS12-381 G2 points (`Vec<BytesObject>`) by a vector of scalars (`Vec<U256Val>`) , and returns the resulting G2 point in bytes format.",
"min_supported_protocol": 22
},
{
"export": "e",
"name": "bls12_381_map_fp2_to_g2",
"args": [
{
"name": "fp2",
"type": "BytesObject"
}
],
"return": "BytesObject",
"docs": "Maps a BLS12-381 quadratic extension field element (Fp2) to G2 point. Fp2 serialization format: concat(be_bytes(c1), be_bytes(c0))",
"min_supported_protocol": 22
},
{
"export": "f",
"name": "bls12_381_hash_to_g2",
"args": [
{
"name": "msg",
"type": "BytesObject"
},
{
"name": "dst",
"type": "BytesObject"
}
],
"return": "BytesObject",
"docs": "Hashes a message to a BLS12-381 G2 point, with implementation following the specification in [Hashing to Elliptic Curves](https://datatracker.ietf.org/doc/html/rfc9380) (ciphersuite 'BLS12381G2_XMD:SHA-256_SSWU_RO_'). `dst` is the domain separation tag that will be concatenated with the `msg` during hashing, it is intended to keep hashing inputs of different applications separate. It is required `0 < len(dst_bytes) < 256`. DST **must** be chosen with care to avoid compromising the application's security properties. Refer to section 3.1 in the RFC on requirements of DST.",
"min_supported_protocol": 22
},
{
"export": "g",
"name": "bls12_381_multi_pairing_check",
"args": [
{
"name": "vp1",
"type": "VecObject"
},
{
"name": "vp2",
"type": "VecObject"
}
],
"return": "Bool",
"docs": "performs pairing operation on a vector of `G1` (`Vec<BytesObject>`) and a vector of `G2` points (`Vec<BytesObject>`) , return true if the result equals `1_fp12`",
"min_supported_protocol": 22
},
{
"export": "h",
"name": "bls12_381_fr_add",
"args": [
{
"name": "lhs",
"type": "U256Val"
},
{
"name": "rhs",
"type": "U256Val"
}
],
"return": "U256Val",
"docs": "performs addition `(lhs + rhs) mod r` between two BLS12-381 scalar elements (Fr), where r is the subgroup order",
"min_supported_protocol": 22
},
{
"export": "i",
"name": "bls12_381_fr_sub",
"args": [
{
"name": "lhs",
"type": "U256Val"
},
{
"name": "rhs",
"type": "U256Val"
}
],
"return": "U256Val",
"docs": "performs subtraction `(lhs - rhs) mod r` between two BLS12-381 scalar elements (Fr), where r is the subgroup order",
"min_supported_protocol": 22
},
{
"export": "j",
"name": "bls12_381_fr_mul",
"args": [
{
"name": "lhs",
"type": "U256Val"
},
{
"name": "rhs",
"type": "U256Val"
}
],
"return": "U256Val",
"docs": "performs multiplication `(lhs * rhs) mod r` between two BLS12-381 scalar elements (Fr), where r is the subgroup order",
"min_supported_protocol": 22
},
{
"export": "k",
"name": "bls12_381_fr_pow",
"args": [
{
"name": "lhs",
"type": "U256Val"
},
{
"name": "rhs",
"type": "U64Val"
}
],
"return": "U256Val",
"docs": "performs exponentiation of a BLS12-381 scalar element (Fr) with a u64 exponent i.e. `lhs.exp(rhs) mod r`, where r is the subgroup order",
"min_supported_protocol": 22
},
{
"export": "l",
"name": "bls12_381_fr_inv",
"args": [
{
"name": "lhs",
"type": "U256Val"
}
],
"return": "U256Val",
"docs": "performs inversion of a BLS12-381 scalar element (Fr) modulo r (the subgroup order)",
"min_supported_protocol": 22
}
]
},
Expand Down
12 changes: 12 additions & 0 deletions soroban-env-common/src/num.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,12 @@ impl TryFrom<i128> for I128Small {
}
}

impl From<U256Small> for u64 {
fn from(value: U256Small) -> Self {
value.0.get_body()
}
}

impl TryFrom<U256> for U256Small {
type Error = ConversionError;
fn try_from(value: U256) -> Result<Self, Self::Error> {
Expand All @@ -243,6 +249,12 @@ impl TryFrom<U256> for U256Small {
}
}

impl From<I256Small> for i64 {
fn from(value: I256Small) -> Self {
value.0.get_signed_body()
}
}

impl TryFrom<I256> for I256Small {
type Error = ConversionError;
fn try_from(value: I256) -> Result<Self, Self::Error> {
Expand Down
Loading

0 comments on commit 0497816

Please sign in to comment.