The built-in enum HashAlgorithm
provides the set of hashing algorithms that
are supported by the language natively.
1pub enum HashAlgorithm: UInt8 {2/// SHA2_256 is SHA-2 with a 256-bit digest (also referred to as SHA256).3pub case SHA2_256 = 145/// SHA2_384 is SHA-2 with a 384-bit digest (also referred to as SHA384).6pub case SHA2_384 = 278/// SHA3_256 is SHA-3 with a 256-bit digest.9pub case SHA3_256 = 31011/// SHA3_384 is SHA-3 with a 384-bit digest.12pub case SHA3_384 = 41314/// KMAC128_BLS_BLS12_381 is an instance of KECCAK Message Authentication Code (KMAC128) mac algorithm.15/// Although this is a MAC algorithm, KMAC is included in this list as it can be used hash16/// when the key is used a non-public customizer.17/// KMAC128_BLS_BLS12_381 is used in particular as the hashing algorithm for the BLS signature scheme on the curve BLS12-381.18/// It is a customized version of KMAC128 that is compatible with the hashing to curve19/// used in BLS signatures.20/// It is the same hasher used by signatures in the internal Flow protocol.21pub case KMAC128_BLS_BLS12_381 = 52223/// KECCAK_256 is the legacy Keccak algorithm with a 256-bits digest, as per the original submission to the NIST SHA3 competition.24/// KECCAK_256 is different than SHA3 and is used by Ethereum.25pub case KECCAK_256 = 62627/// Returns the hash of the given data28pub fun hash(_ data: [UInt8]): [UInt8]2930/// Returns the hash of the given data and tag31pub fun hashWithTag(_ data: [UInt8], tag: string): [UInt8]32}
The hash algorithms provide two ways to hash input data into digests, hash
and hashWithTag
.
hash
hashes the input data using the chosen hashing algorithm.
KMAC
is the only MAC algorithm on the list
and configured with specific parameters (detailed in KMAC128 for BLS)
For example, to compute a SHA3-256 digest:
1let data: [UInt8] = [1, 2, 3]2let digest = HashAlgorithm.SHA3_256.hash(data)
hashWithTag
hashes the input data along with an input tag.
It allows instanciating independent hashing functions customized with a domain separation tag (DST).
For most of the hashing algorithms, mixing the data with the tag is done by pre-fixing the data with the tag and
hashing the result.
SHA2_256
,SHA2_384
,SHA3_256
,SHA3_384
,KECCAK_256
: If the tag is non-empty, the hashed message isbytes(tag) || data
wherebytes()
is the UTF-8 encoding of the input string, padded with zeros till 32 bytes. Therefore tags must not exceed 32 bytes. If the tag used is empty, no data prefix is applied, and the hashed message is simplydata
(same ashash
output).KMAC128_BLS_BLS12_381
: refer to KMAC128 for BLS for details.
KMAC128_BLS_BLS12_381
is an instance of the cSHAKE-based KMAC128.
Although this is a MAC algorithm, KMAC can be used as a hash when the key is used as a non-private customizer.
KMAC128_BLS_BLS12_381
is used in particular as the hashing algorithm for the BLS signature scheme on the curve BLS12-381.
It is a customized instance of KMAC128 and is compatible with the hashing to curve used by BLS signatures.
It is the same hasher used by the internal Flow protocol, and can be used to verify Flow protocol signatures on-chain.
To define the MAC instance, KMAC128(customizer, key, data, length)
is instanciated with the following parameters
(as referred to by the NIST SHA-3 Derived Functions):
customizer
is the UTF-8 encoding of"H2C"
.key
is the UTF-8 encoding of"FLOW--V00-CS00-with-BLS_SIG_BLS12381G1_XOF:KMAC128_SSWU_RO_POP_"
whenhash
is used. It includes the inputtag
whenhashWithTag
is used and key becomes the UTF-8 encoding of"FLOW-" || tag || "-V00-CS00-with-BLS_SIG_BLS12381G1_XOF:KMAC128_SSWU_RO_POP_"
.data
is the input data to hash.length
is 1024 bytes.
The built-in enum SignatureAlgorithm
provides the set of signing algorithms that
are supported by the language natively.
1pub enum SignatureAlgorithm: UInt8 {2/// ECDSA_P256 is ECDSA on the NIST P-256 curve.3pub case ECDSA_P256 = 145/// ECDSA_secp256k1 is ECDSA on the secp256k1 curve.6pub case ECDSA_secp256k1 = 278/// BLS_BLS12_381 is BLS signature scheme on the BLS12-381 curve.9/// The scheme is set-up so that signatures are in G_1 (subgroup of the curve over the prime field)10/// while public keys are in G_2 (subgroup of the curve over the prime field extension).11pub case BLS_BLS12_381 = 312}
PublicKey
is a built-in structure that represents a cryptographic public key of a signature scheme.
1struct PublicKey {2let publicKey: [UInt8]3let signatureAlgorithm: SignatureAlgorithm45/// Verifies a signature under the given tag, data and public key.6/// It uses the given hash algorithm to hash the tag and data.7pub fun verify(8signature: [UInt8],9signedData: [UInt8],10domainSeparationTag: String,11hashAlgorithm: HashAlgorithm12): Bool1314/// Verifies the proof of possession of the private key.15/// This function is only implemented if the signature algorithm16/// of the public key is BLS (BLS_BLS12_381).17/// If called with any other signature algorithm, the program aborts18pub fun verifyPoP(_ proof: [UInt8]): Bool19}
PublicKey
supports two methods verify
and verifyPoP
.
verifyPoP
will be covered under BLS multi-signature.
A PublicKey
can be constructed using the raw key and the signing algorithm.
1let publicKey = PublicKey(2publicKey: "010203".decodeHex(),3signatureAlgorithm: SignatureAlgorithm.ECDSA_P2564)
The raw key value depends on the supported signature scheme:
-
ECDSA_P256
andECDSA_secp256k1
: The public key is an uncompressed curve point(X,Y)
whereX
andY
are two prime field elements. The raw key is represented asbytes(X) || bytes(Y)
, where||
is the concatenation operation, andbytes()
is the bytes big-endian encoding left padded by zeros to the byte-length of the field prime. The raw public key is 64-bytes long. -
BLS_BLS_12_381
: The public key is a G_2 (curve over the prime field extension) element. The encoding follows the compressed serialization defined in the IETF draft-irtf-cfrg-pairing-friendly-curves-08. A public key is 96-bytes long.
A public key is validated at the time of creation. Only valid public keys can be created. The validation of the public key depends on the supported signature scheme:
-
ECDSA_P256
andECDSA_secp256k1
: The givenX
andY
coordinates are correctly serialized, represent valid prime field elements, and the resulting point is on the correct curve (no subgroup check needed since the cofactor of both supported curves is 1). -
BLS_BLS_12_381
: The given key is correctly serialized following the compressed serialization in IETF draft-irtf-cfrg-pairing-friendly-curves-08. The coordinates represent valid prime field extension elemnents. The resulting point is on the curve, and is on the correct subgroup G_2.
Since the validation happen only at the time of creation, public keys are immutable.
1publicKey.signatureAlgorithm = SignatureAlgorithm.ECDSA_secp256k1 // Not allowed2publicKey.publicKey = [] // Not allowed34publicKey.publicKey[2] = 4 // No effect
Invalid public keys cannot be constructed so public keys are always valid.
A signature can be verified using the verify
function of the PublicKey
:
1let pk = PublicKey(2publicKey: "96142CE0C5ECD869DC88C8960E286AF1CE1B29F329BA4964213934731E65A1DE480FD43EF123B9633F0A90434C6ACE0A98BB9A999231DB3F477F9D3623A6A4ED".decodeHex(),3signatureAlgorithm: SignatureAlgorithm.ECDSA_P2564)56let signature = "108EF718F153CFDC516D8040ABF2C8CC7AECF37C6F6EF357C31DFE1F7AC79C9D0145D1A2F08A48F1A2489A84C725D6A7AB3E842D9DC5F8FE8E659FFF5982310D".decodeHex()7let message : [UInt8] = [1, 2, 3]89let isValid = pk.verify(10signature: signature,11signedData: message,12domainSeparationTag: "",13hashAlgorithm: HashAlgorithm.SHA2_25614)15// `isValid` is false
The inputs to verify
depend on the signature scheme used:
- ECDSA (
ECDSA_P256
andECDSA_secp256k1
):signature
expects the couple(r,s)
. It is serialized asbytes(r) || bytes(s)
, where||
is the concatenation operation, andbytes()
is the bytes big-endian encoding left padded by zeros to the byte-length of the curve order. The signature is 64 bytes-long for both curves.signedData
is the arbitrary message to verify the signature against.domainSeparationTag
is the expected domain tag. Multiple valid tag values can be used (checkhashWithTag
for more details).hashAlgorithm
is eitherSHA2_256
,SHA3_256
orKECCAK_256
. It is the algorithm used to hash the message along with the given tag (check thehashWithTag
function for more details).
As noted in hashWithTag
for SHA2_256
, SHA3_256
and KECCAK_256
, using an empty tag
results in hashing the input data only. If a signature verification
needs to be done against data without any domain tag, this can be done by using an empty domain tag ""
.
ECDSA verification is implemented as defined in ANS X9.62 (also referred by FIPS 186-4 and SEC 1, Version 2.0).
A valid signature would be generated using the expected signedData
, domainSeparationTag
and hashAlgorithm
used to verify.
- BLS (
BLS_BLS_12_381
):signature
expects a G_1 (subgroup of the curve over the prime field) point. The encoding follows the compressed serialization defined in the IETF draft-irtf-cfrg-pairing-friendly-curves-08. A signature is 48-bytes long.signedData
is the arbitrary message to verify the signature against.domainSeparationTag
is the expected domain tag. All tags are accepted (check KMAC128 for BLS).hashAlgorithm
only acceptsKMAC128_BLS_BLS12_381
. It is the algorithm used to hash the message along with the given tag (check KMAC128 for BLS).
BLS verification performs the necessary membership check of the signature while the membership check of the public key is performed at the creation of the PublicKey
object
and not repeated during the signature verification.
The verificaction uses a hash-to-curve algorithm to hash the signedData
into a G_1
point, following the hash_to_curve
method described in the draft-irtf-cfrg-hash-to-curve-14.
While KMAC128 is used as a hash-to-field method resulting in two field elements, the mapping to curve is implemented using the simplified SWU.
A valid signature would be generated using the expected signedData
and domainSeparationTag
, as well the same hashing to curve process.
BLS signature scheme allows efficient multi-signature features. Multiple signatures can be aggregated
into a single signature which can be verified against an aggregated public key. This allows authenticating
multiple signers with a single signature verification.
While BLS provides multiple aggregation techniques,
Cadence supports basic aggregation tools that cover a wide list of use-cases.
These tools are defined in the built-in BLS
contract, which does not need to be imported.
Multi-signature verification in BLS requires a defense against rogue public-key attacks. Multiple ways are
available to protect BLS verification. The language provides the proof of possession of private key as a defense tool.
The proof of possession of private key is a BLS signature over the public key itself.
The PoP signature follows the same requirements of a BLS signature (detailed in Signature verification),
except it uses a special domain separation tag. The key expected to be used in KMAC128 is the UTF-8 encoding of "BLS_POP_BLS12381G1_XOF:KMAC128_SSWU_RO_POP_"
.
The expected message to be signed by the PoP is the serialization of the BLS public key corresponding to the signing private key (serialization details).
The PoP can only be verified using the PublicKey
method verifyPoP
.
1fun aggregateSignatures(_ signatures: [[UInt8]]): [UInt8]?
Aggregates multiple BLS signatures into one. Signatures could be generated from the same or distinct messages, they could also be the aggregation of other signatures. The order of the signatures in the slice does not matter since the aggregation is commutative. There is no subgroup membership check performed on the input signatures. If the array is empty or if decoding one of the signature fails, the program aborts
The output signature can be verified against an aggregated public key to authenticate multiple
signers at once. Since the verify
method accepts a single data to verify against, it is only possible to
verfiy multiple signatures of the same message.
1fun aggregatePublicKeys(_ publicKeys: [PublicKey]): PublicKey?
Aggregates multiple BLS public keys into one.
The order of the public keys in the slice does not matter since the aggregation is commutative. The input keys are guaranteed to be in the correct subgroup since subgroup membership is checked at the key creation time. If the array is empty or any of the input keys is not a BLS key, the program aborts
The output public key can be used to verify aggregated signatures to authenticate multiple
signers at once. Since the verify
method accepts a single data to verify against, it is only possible to
verfiy multiple signatures of the same message.
The built-in contract Crypto
can be used to perform cryptographic operations.
The contract can be imported using import Crypto
.
The crypto contract also allows creating key lists to be used for multi-signature verification. For example, to verify two signatures with equal weights for some signed data:
1import Crypto23pub fun test main() {4let keyList = Crypto.KeyList()56let publicKeyA = PublicKey(7publicKey:8"db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(),9signatureAlgorithm: SignatureAlgorithm.ECDSA_P25610)11keyList.add(12publicKeyA,13hashAlgorithm: HashAlgorithm.SHA3_256,14weight: 0.515)1617let publicKeyB = PublicKey(18publicKey:19"df9609ee588dd4a6f7789df8d56f03f545d4516f0c99b200d73b9a3afafc14de5d21a4fc7a2a2015719dc95c9e756cfa44f2a445151aaf42479e7120d83df956".decodeHex(),20signatureAlgorithm: SignatureAlgorithm.ECDSA_P25621)22keyList.add(23publicKeyB,24hashAlgorithm: HashAlgorithm.SHA3_256,25weight: 0.526)2728let signatureSet = [29Crypto.KeyListSignature(30keyIndex: 0,31signature:32"8870a8cbe6f44932ba59e0d15a706214cc4ad2538deb12c0cf718d86f32c47765462a92ce2da15d4a29eb4e2b6fa05d08c7db5d5b2a2cd8c2cb98ded73da31f6".decodeHex()33),34Crypto.KeyListSignature(35keyIndex: 1,36signature:37"bbdc5591c3f937a730d4f6c0a6fde61a0a6ceaa531ccb367c3559335ab9734f4f2b9da8adbe371f1f7da913b5a3fdd96a871e04f078928ca89a83d841c72fadf".decodeHex()38)39]4041// "foo", encoded as UTF-8, in hex representation42let signedData = "666f6f".decodeHex()4344let isValid = keyList.verify(45signatureSet: signatureSet,46signedData: signedData47)48}
The API of the Crypto contract related to key lists is:
1pub struct KeyListEntry {2pub let keyIndex: Int3pub let publicKey: PublicKey4pub let hashAlgorithm: HashAlgorithm5pub let weight: UFix646pub let isRevoked: Bool78init(9keyIndex: Int,10publicKey: PublicKey,11hashAlgorithm: HashAlgorithm,12weight: UFix64,13isRevoked: Bool14)15}1617pub struct KeyList {1819init()2021/// Adds a new key with the given weight22pub fun add(23_ publicKey: PublicKey,24hashAlgorithm: HashAlgorithm,25weight: UFix6426)2728/// Returns the key at the given index, if it exists.29/// Revoked keys are always returned, but they have `isRevoked` field set to true30pub fun get(keyIndex: Int): KeyListEntry?3132/// Marks the key at the given index revoked, but does not delete it33pub fun revoke(keyIndex: Int)3435/// Returns true if the given signatures are valid for the given signed data36pub fun verify(37signatureSet: [KeyListSignature],38signedData: [UInt8]39): Bool40}4142pub struct KeyListSignature {43pub let keyIndex: Int44pub let signature: [UInt8]4546pub init(keyIndex: Int, signature: [UInt8])47}