- Go back to TOP(インデックスページへ)
- Hash algorithms
- Hashing
- Hashing with a domain tag
- Signature algorithms
- Public keys
- BLS multi-signature
- Crypto Contract
Hash algorithms
組み込みの列挙型HashAlgorithmは、サポートされているハッシュアルゴリズムのセットを提供します。
access(all)
enum HashAlgorithm: UInt8 {
/// SHA2_256 is SHA-2 with a 256-bit digest (also referred to as SHA256).
access(all)
case SHA2_256 = 1
/// SHA2_384 is SHA-2 with a 384-bit digest (also referred to as SHA384).
access(all)
case SHA2_384 = 2
/// SHA3_256 is SHA-3 with a 256-bit digest.
access(all)
case SHA3_256 = 3
/// SHA3_384 is SHA-3 with a 384-bit digest.
access(all)
case SHA3_384 = 4
/// KMAC128_BLS_BLS12_381 is an instance of KECCAK Message Authentication Code (KMAC128) mac algorithm.
/// Although this is a MAC algorithm, KMAC is included in this list as it can be used hash
/// when the key is used a non-public 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 version of KMAC128 that is compatible with the hashing to curve
/// used in BLS signatures.
/// It is the same hasher used by signatures in the internal Flow protocol.
access(all)
case KMAC128_BLS_BLS12_381 = 5
/// KECCAK_256 is the legacy Keccak algorithm with a 256-bits digest, as per the original submission to the NIST SHA3 competition.
/// KECCAK_256 is different than SHA3 and is used by Ethereum.
access(all)
case KECCAK_256 = 6
/// Returns the hash of the given data
access(all)
view fun hash(_ data: [UInt8]): [UInt8]
/// Returns the hash of the given data and tag
access(all)
view fun hashWithTag(_ data: [UInt8], tag: string): [UInt8]
}
ハッシュアルゴリズムは、入力データをダイジェストにハッシュ化する2つの方法、hashおよびhashWithTagを提供します。
Hashing
hashは、選択したハッシュアルゴリズムを使用して入力データをハッシュ化します。KMACはリスト上の唯一のMACアルゴリズムであり、特定のパラメータ(KMAC128 for BLSで詳細に説明)で構成されています。
例えば、SHA3-256ダイジェストを計算するには:
let data: [UInt8] = [1, 2, 3]
let digest = HashAlgorithm.SHA3_256.hash(data)
Hashing with a domain tag
hashWithTag は入力タグとともに、入力データをハッシュ化します。 ドメイン分離タグ(DST)でカスタマイズされた独立したハッシュ関数のインスタンス化が可能です。 ほとんどのハッシュアルゴリズムでは、タグとデータを混ぜ合わせるために、事前にタグをデータに付加し、結果をハッシュ化します。
- SHA2_256、SHA2_384、SHA3_256、SHA3_384、KECCAK_256:タグが空ではない場合、ハッシュ化されたメッセージは、bytes(tag) || dataとなります。ここで、bytes()は、32バイトになるまでゼロで埋められた入力文字列のUTF-8エンコードです。したがって、タグは32バイトを超えてはなりません。使用するタグが空の場合、データプレフィックスは適用されず、ハッシュ化されたメッセージは単純にデータとなります(hashの出力と同じ)。
- KMAC128_BLS_BLS12_381:詳細はKMAC128 for BLSを参照してください。
KMAC128 for BLS
KMAC128_BLS_BLS12_381は、cSHAKEベースのKMAC128のインスタンスです。これはMACアルゴリズムですが、キーを非プライベートカスタマイザーとして使用する場合、KMACをハッシュとして使用できます。KMAC128_BLS_BLS12_381は、特に曲線BLS12-381のBLS署名スキームのハッシュアルゴリズムとして使用されます。これはKMAC128のカスタマイズされたインスタンスであり、BLS署名で使用される曲線へのハッシュと互換性があります。これは内部フロープロトコルで使用されるハッシュと同じものであり、Cadenceのフロープロトコル署名の検証に使用できます。
MACインスタンスを定義するには、KMAC128(カスタマイザー、キー、データ、長さ)を次のパラメータでインスタンス化します(NIST SHA-3派生関数を参照)。
- customizerは、UTF-8エンコーディングの「H2C」です。
- keyは、「FLOW--V00-CS00-with-BLS_SIG_BLS12381G1_XOF:KMAC128_SSWU_RO_POP-」のUTF-8エンコーディングです。hashが使用される場合、hashWithTagを使用し、キーがUTF-8エンコーディングの「FLOW-」 || tag || 「-V00-CS00-with-BLS_SIG_BLS12381G1_XOF:KMAC128_SSWU_RO_POP_」の場合、入力tagが含まれます。
- dataはハッシュへの入力データです。
- lengthは1024バイトです。
Signature algorithms
組み込みの列挙型SignatureAlgorithmは、サポートされている署名アルゴリズムのセットを提供します。
access(all)
enum SignatureAlgorithm: UInt8 {
/// ECDSA_P256 is ECDSA on the NIST P-256 curve.
access(all)
case ECDSA_P256 = 1
/// ECDSA_secp256k1 is ECDSA on the secp256k1 curve.
access(all)
case ECDSA_secp256k1 = 2
/// BLS_BLS12_381 is BLS signature scheme on the BLS12-381 curve.
/// The scheme is set-up so that signatures are in G_1 (subgroup of the curve over the prime field)
/// while public keys are in G_2 (subgroup of the curve over the prime field extension).
access(all)
case BLS_BLS12_381 = 3
}
Public keys
PublicKey は、署名スキームの暗号化公開鍵を表す組み込み構造体です。
access(all)
struct PublicKey {
access(all)
let publicKey: [UInt8]
access(all)
let signatureAlgorithm: SignatureAlgorithm
/// Verifies a signature under the given tag, data and public key.
/// It uses the given hash algorithm to hash the tag and data.
access(all)
view fun verify(
signature: [UInt8],
signedData: [UInt8],
domainSeparationTag: String,
hashAlgorithm: HashAlgorithm
): Bool
/// Verifies the proof of possession of the private key.
/// This function is only implemented if the signature algorithm
/// of the public key is BLS (BLS_BLS12_381).
/// If called with any other signature algorithm, the program aborts
access(all)
view fun verifyPoP(_ proof: [UInt8]): Bool
// creating a PublicKey is a view operation
access(all)
view init()
}
PublicKeyは、verifyとverifyPoPという2つの方法に対応しています。
verufyは、署名検証機能であり、一方、verufyPoPは、BLSマルチ署名に含まれます。
Public Key construction
PublicKeyは、raw keyと署名アルゴリズムによって構成される。
let publicKey = PublicKey(
publicKey: "010203".decodeHex(),
signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
)
raw key値は、サポートされている署名スキームによって異なります。
- ECDSA_P256およびECDSA_secp256k1:公開鍵は、圧縮されていない曲線点(X,Y)であり、ここでXおよびYは2つの素体要素です。生の鍵は、bytes(X) || bytes(Y)として表されます。ここで、||は結合操作であり、bytes()はバイトビッグエンディアンエンコーディングで、左詰めされ、素体素数のバイト長までゼロで埋められます。生の公開鍵は64バイトの長さです。
- BLS_BLS_12_381:公開鍵はG_2要素(素体拡大上の曲線)です。エンコードは、IETFのドラフト-irtf-cfrg-pairing-friendly-curves-08で定義されている圧縮シリアライゼーションに従います。公開鍵は96バイトの長さです。
Public Key validation
公開鍵は作成時に検証されます。有効な公開鍵のみ作成できます。公開鍵の検証は、サポートされている署名スキームによって異なります。
- ECDSA_P256およびECDSA_secp256k1:指定されたXおよびY座標が正しくシリアライズされ、有効な素体要素を表し、結果として得られる点が正しい曲線上にあること(サポートされている両曲線の共因子は1であるため、サブグループのチェックは不要)。
- BLS_BLS_12_381: 指定されたキーは、IETF draft-irtf-cfrg-pairing-friendly-curves-08 の圧縮シリアライゼーションに従って正しくシ このような同一キーは、複数のキーを統合する際に役立ちます。
検証は作成時にのみ行われるため、公開キーは不変です。
publicKey.signatureAlgorithm = SignatureAlgorithm.ECDSA_secp256k1 // Not allowed
publicKey.publicKey = [] // Not allowed
publicKey.publicKey[2] = 4 // No effect
無効な公開鍵は作成できないため、公開鍵は常に有効です。
Signature verification
署名は、PublicKeyのverify関数を使用して検証できます。
let pk = PublicKey(
publicKey: "96142CE0C5ECD869DC88C8960E286AF1CE1B29F329BA4964213934731E65A1DE480FD43EF123B9633F0A90434C6ACE0A98BB9A999231DB3F477F9D3623A6A4ED".decodeHex(),
signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
)
let signature = "108EF718F153CFDC516D8040ABF2C8CC7AECF37C6F6EF357C31DFE1F7AC79C9D0145D1A2F08A48F1A2489A84C725D6A7AB3E842D9DC5F8FE8E659FFF5982310D".decodeHex()
let message : [UInt8] = [1, 2, 3]
let isValid = pk.verify(
signature: signature,
signedData: message,
domainSeparationTag: "",
hashAlgorithm: HashAlgorithm.SHA2_256
)
// `isValid` is false
検証する入力は、使用する署名方式によって異なります。
- ECDSA (ECDSA_P256およびECDSA_secp256k1):
署名は、(r,s)のペアを必要とします。これは、bytes(r) || bytes(s)としてシリアライズされます。ここで、||は結合操作、bytes()はバイトビッグエンディアンエンコーディングで、曲線の次数のバイト長までゼロで埋められます。署名は両方の曲線に対して64バイトの長さです。 - signedDataは署名を検証するための任意のメッセージです。
- domainSeparationTagは想定されるドメインタグ、すなわち、正しく生成された署名が使用することが想定される値です。ドメインタグは、署名の生成または検証のハッシュ化ステップの前にメッセージの前に付加されます(詳細はhashWithTagを参照)。タグの目的は、異なるコンテキストやドメインを分離し、署名が元のコンテキスト以外の異なるコンテキストで再利用されないようにすることです。アプリケーションは、ユーザーの署名を他のアプリケーションの署名と区別するために、独自の任意のドメインタグ値を定義する必要があります。アプリケーションタグは、有効な署名の生成と検証に適用する必要があります。文字列値に関する要件については、「ドメインタグによるハッシュ化」を参照してください。
- hashAlgorithmは、SHA2_256、SHA3_256、またはKECCAK_256のいずれかです。これは、指定されたタグとともにメッセージをハッシュ化するために使用されるアルゴリズムです(「タグによるハッシュ化」を参照)。
hashWithTag for SHA2_256、SHA3_256、およびKECCAK_256で述べられているように、空のtagを使用すると入力データのみがハッシュ化されます。ドメインタグのないデータに対して署名検証を計算する必要がある場合は、空のドメインタグ""を渡す必要があります。
ECDSA検証は、ANS X9.62(FIPS 186-4およびSEC 1、バージョン2.0でも参照)で定義されているとおりに実装されています。検証に使用するsignedData、domainSeparationTag、hashAlgorithmを使用して、有効な署名が生成されます。
- BLS (BLS_BLS_12_381):
署名はG_1点(素体上の曲線)を必要とします。エンコードは、IETF draft-irtf-cfrg-pairing-friendly-curves-08で定義された圧縮シリアライゼーションに従います。署名の長さは48バイトです。 - signedDataは、署名を検証するための任意のメッセージです。
- domainSeparationTagは、想定されるドメインタグ、すなわち、正しく生成された署名が使用することが想定される値です。ドメインタグは、KMAC128 for BLSで指定されているように、署名の生成または検証中にメッセージと混合されます。このタグの目的は、異なるコンテキストまたはドメインを分離し、署名が元のコンテキスト以外の異なるコンテキストで再利用されないようにすることです。アプリケーションは、ユーザーの署名を他のアプリケーションの署名と区別するために、独自の任意のドメインタグ値を定義すべきです。アプリケーションタグは、有効な署名の生成と検証に適用すべきです。すべての文字列値は、BLSのタグとして有効です(BLS用のKMAC128を参照)。
- hashAlgorithmは、KMAC128_BLS_BLS12_381のみを受け入れます。これは、指定されたタグとともにメッセージをハッシュ化するために使用されるアルゴリズムです(KMAC128 for BLS を参照)。
BLS 検証では、署名に必要なメンバーシップチェックが実行され、公開鍵のメンバーシップチェックはPublicKeyオブジェクトの作成時に実行され、署名検証中に繰り返されることはありません。 同一性に関する問題を回避するために、同一性公開鍵による検証は常にfalse を返します。
検証には、ハッシュ・トゥ・カーブ・アルゴリズムを使用して、signedData をG_1ポイントにハッシュ化します。これは、draft-irtf-cfrg-hash-to-curve-14 で説明されている hash_to_curveメソッドに従っています。 KMAC128 がハッシュ・トゥ・フィールド・メソッドとして使用され、2つのフィールド要素が生成される一方で、カーブへのマッピングは、簡略化された SWU を使用して実装されています。
有効な署名は、期待されるsignedDataおよびdomainSeparationTag、さらに同じ曲線ハッシュ処理を使用して生成されるべきです。
BLS multi-signature
BLS署名スキームは、効率的な複数署名機能を提供します。複数の署名を1つの署名に集約し、集約された公開鍵に対して検証することができます。これにより、1つの署名検証で複数の署名者を認証することができます。BLSは複数の集約技術を提供しますが、Cadenceは幅広いユースケースに対応する基本的な集約ツールをサポートしています。これらのツールは、組み込みのBLSスマートコントラクトで定義されており、インポートする必要はありません。
Proof-of-Possession
BLSにおけるマルチシグネチャ検証では、不正な公開鍵攻撃に対する防御が必要です。BLS検証を保護するには、複数の方法があります。Cadenceは防御ツールとして秘密鍵の所有証明を提供しています。秘密鍵の所有証明は、公開鍵自体に対するBLS署名です。PoP署名は、特別なドメイン分離タグを使用する以外は、BLS署名と同じ要件に従います(詳細は「署名検証」を参照)。KMAC128で使用されると予想される鍵は、UTF-8エンコーディングされた「BLS_POP_BLS12381G1_XOF:KMAC128_SSWU_RO_POP_」です。PoPによって署名されると予想されるメッセージは、署名用の秘密鍵に対応するBLS公開鍵のシリアル化です(シリアル化の詳細)。PoPは、PublicKeyメソッドのverifyPoPを使用してのみ検証できます。
BLS signature aggregation
view fun aggregateSignatures(_ signatures: [[UInt8]]): [UInt8]?
複数のBLS署名を1つに集約する。署名は同一または異なるメッセージから生成される可能性があり、他の署名を集約したものである可能性もある。集約は交換可能であるため、スライス内の署名の順序は重要ではない。入力署名に対してサブグループのメンバーシップチェックは実行されない。配列が空の場合、または署名の1つのデコードに失敗した場合、プログラムは中止する。
出力署名は、集約された公開鍵と照合することで、複数の署名者を一度に認証することができます。検証メソッドは検証対象として単一のデータを受け付けるため、同じメッセージの複数の署名のみを検証することができます。
BLS public key aggregation
view fun aggregatePublicKeys(_ publicKeys: [PublicKey]): PublicKey?
複数のBLS公開鍵を1つに集約します。
集約は交換可能であるため、スライス内の公開鍵の順序は重要ではありません。入力鍵は、鍵の作成時にサブグループのメンバーシップが確認されるため、正しいサブグループに属することが保証されます。配列が空の場合、または入力鍵のいずれかがBLS鍵でない場合、プログラムは中断します。 識別公開鍵は、この関数への有効な入力であり、集約の識別要素を表すことに注意してください。
出力される公開鍵は、複数の署名者を一括して認証するために集約署名を検証する際に使用できます。検証メソッドは検証対象の単一データを受け入れるため、同じメッセージの複数の署名のみ検証が可能です。 同一性公開鍵は、この関数の出力として生成される可能性がありますが、同一性に対する署名の検証結果は偽となります。
集約署名を検証する際に不正な鍵攻撃を防止するためには、集約プロセスに関与する個々の鍵の公開鍵を検証することが重要です。
Crypto Contract
組み込み済みスマートコントラクトCryptoは、暗号操作を実行するために使用できます。このスマートコントラクトは、import Cryptoを使用してインポートできます。
Key Lists
暗号コントラクトでは、マルチシグネチャ認証に使用するキーリストを作成することができます。
キーリストは基本的に公開鍵のリストであり、各公開鍵にはキーインデックス、ハッシュアルゴリズム、および重みが割り当てられています。KeyListSignatureのリストは、各署名エントリで使用する公開鍵インデックスを指定するキーリストに対して検証することができます。リストの検証は、リスト内のすべての署名が有効であり、各公開鍵が最大でも1回だけ使用され、使用されたキーの重みが合計で少なくとも1以上になる場合に成功します。
各署名の検証には、キーエントリーハッシュアルゴリズムと入力ドメイン分離タグを使用したCadenceの単一署名検証関数(詳細はsignature verificationを参照)が使用されます。
公開鍵を無効にすることで、その公開鍵を無効にすることができます。無効にされた鍵はリストに残り、同じインデックスを保持します。無効にされていない鍵に対する署名のみが有効とみなされます。
例えば、ある署名済みデータに対する重みが等しい2つの署名を検証するには、以下のようになります。
import Crypto
access(all)
fun test main() {
let keyList = Crypto.KeyList()
let publicKeyA = PublicKey(
publicKey:
"db04940e18ec414664ccfd31d5d2d4ece3985acb8cb17a2025b2f1673427267968e52e2bbf3599059649d4b2cce98fdb8a3048e68abf5abe3e710129e90696ca".decodeHex(),
signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
)
keyList.add(
publicKeyA,
hashAlgorithm: HashAlgorithm.SHA3_256,
weight: 0.5
)
let publicKeyB = PublicKey(
publicKey:
"df9609ee588dd4a6f7789df8d56f03f545d4516f0c99b200d73b9a3afafc14de5d21a4fc7a2a2015719dc95c9e756cfa44f2a445151aaf42479e7120d83df956".decodeHex(),
signatureAlgorithm: SignatureAlgorithm.ECDSA_P256
)
keyList.add(
publicKeyB,
hashAlgorithm: HashAlgorithm.SHA3_256,
weight: 0.5
)
let signatureSet = [
Crypto.KeyListSignature(
keyIndex: 0,
signature:
"8870a8cbe6f44932ba59e0d15a706214cc4ad2538deb12c0cf718d86f32c47765462a92ce2da15d4a29eb4e2b6fa05d08c7db5d5b2a2cd8c2cb98ded73da31f6".decodeHex()
),
Crypto.KeyListSignature(
keyIndex: 1,
signature:
"bbdc5591c3f937a730d4f6c0a6fde61a0a6ceaa531ccb367c3559335ab9734f4f2b9da8adbe371f1f7da913b5a3fdd96a871e04f078928ca89a83d841c72fadf".decodeHex()
)
]
// "foo", encoded as UTF-8, in hex representation
let signedData = "666f6f".decodeHex()
let isValid = keyList.verify(
signatureSet: signatureSet,
signedData: signedData,
domainSeparationTag: "FLOW-V0.0-user",
)
}
以下は、keyのリストと署名リストの実装の詳細です。
access(all)
struct KeyListEntry {
access(all)
let keyIndex: Int
access(all)
let publicKey: PublicKey
access(all)
let hashAlgorithm: HashAlgorithm
access(all)
let weight: UFix64
access(all)
let isRevoked: Bool
init(
keyIndex: Int,
publicKey: PublicKey,
hashAlgorithm: HashAlgorithm,
weight: UFix64,
isRevoked: Bool
)
}
access(all)
struct KeyList {
init()
/// Adds a new key with the given weight
access(all)
fun add(
_ publicKey: PublicKey,
hashAlgorithm: HashAlgorithm,
weight: UFix64
)
/// Returns the key at the given index, if it exists.
/// Revoked keys are always returned, but they have `isRevoked` field set to true
access(all)
fun get(keyIndex: Int): KeyListEntry?
/// Marks the key at the given index revoked, but does not delete it
access(all)
fun revoke(keyIndex: Int)
/// Returns true if the given signatures are valid for the given signed data
/// `domainSeparationTag` is used to specify a scope for each signature,
/// and is implemented the same way as `PublicKey`'s verify function.
access(all)
fun verify(
signatureSet: [KeyListSignature],
signedData: [UInt8],
domainSeparationTag: String
): Bool
}
access(all)
struct KeyListSignature {
access(all)
let keyIndex: Int
access(all)
let signature: [UInt8]
access(all)
init(keyIndex: Int, signature: [UInt8])
}
翻訳元->https://cadence-lang.org/docs/language/crypto