Signatures #


Signatures are cryptographic functions that attest to the origin of a particular message. In the context of Filecoin, signatures are used to send and receive messages with the assurance that each message was generated by a specific entity. In other words, it is infeasible for an entity i to generate a signed message that appears to have been generated by j, with j != i.

Filecoin uses signatures to associate an action to a given party. For example, Filecoin uses signatures in order to validate deal messages which represent an action like a storage deal. Filecoin uses signatures to verify the authenticity of the following objects (non exhaustive list):

  • Messages: Users authenticate their transactions to the blockchain.
  • Tickets: Miner authenticates its ticket (see Storage Miner).
  • Blocks: Block leader signs over all data in the block.

Messages #

To generate a signature for the Message type, compute the signature over the message’s CID (taken as a byte array).

Note: for each specific use of a signature scheme, it is recommended to use a domain separation tag to treat the hash function as an independent random oracle. These tags are indicated in the relevant places throughout the specs. Read more about this in Randomness.

Signature Types #

Filecoin currently uses two types of signatures:

  • ECDSA signatures over the Secp256k1 elliptic curve to authenticate user transactions, mainly for compatibility with external blockchain systems.
  • BLS signatures over the BLS12-381 group of curves

Both signature types fulfill the Signature interface and each type have additional functionality as explained below.

type Message Bytes
type SecretKey Bytes
type PublicKey Bytes
type SignatureBytes Bytes

type SigKeyPair struct {
    PublicKey
    SecretKey
}

type Signature struct {
    Type  SigType         @(internal)
    Sig   SignatureBytes  @(internal)
}

type SigType enum {
    ECDSASigType
    BLSSigType
}

ECDSA Signatures #

Filecoin uses the ECDSA signature algorithm over the secp256k1 curve to authenticate the blockchain transactions. The main reason is to be able to validate transactions from other blockchain systems that uses secp256k1 (such as Bitcoin or exchanges in general). ECDSA signatures offer an additional useful functionality as well: to recover the public key from a given signature. This feature can allow space to be saved on the blockchain by extracting the public key locally from the signature rather than specifying an ID of the public key.

// ECDSA implements the Signature interface using the ECDSA algorithm with
// the Secp256k1 elliptic curve.
type ECDSA struct {
    // The Signature object is the one returned from SigKeyPair.Sign(). It can
    // be casted to ECDSA to get the additional functionality described below.
    Signature

    // Recover recovers a public key associated with a particular signature.
    //
    // Out:
    //    pk - the public key associated with `M` who signed `m`
    //    err - a standard error message indicating any process issues
    //    **
    // In:
    //    m - a series of bytes representing the signed message
    //    sig - a series of bytes representing a signature usually `r`|`s`
    //
    Recover(m Message, sig SignatureBytes) struct {pk PublicKey, err error}
}

Wire Format: Filecoin uses the standard secp256k1 signature serialization, as described below. For more details on how the Filecoin Signature type is serialized, see Signature.

SignatureBytes = [0x30][len][0x02][r][indicator][s][indicator][recovery]

s = Scalar of size 32 bytes

r = Compressed elliptic curve point (x-coordinate) of size 32 bytes

recovery = Information needed to recover a public key from sig.

  • LSB(0) = parity of y-coordinate of r
  • LSB(1) = overflow indicator

indicator = a 2 byte formatting indicator

External References: Elliptic Curve Cryptography Paper

BLS Signatures #

Filecoin uses the BLS signature scheme over the [BLS12-381](BLS12-381](https://electriccoin.co/blog/new-snark-curve/) group of elliptic curves. You can find the default Rust implementation in Filecoin’s repo.

// BLS implements the Signature interface using the BLS signature scheme
// with the BLS12-381 group of elliptic curves.
type BLS struct {
    // This signature is the one returned from SigKeyPair.Sign(). It can be
    // casted to a BLS signature struct to get the additional functionalities.
    Signature

    // This represents the largest potential value for a BLS signature in Bytes
    MaxSigValue() Bytes

    // Aggregates this BLS signature and `sig` into one BLS signature that can
    // be verified against the aggregation of the two public key that signed
    // the aggregated signatures.
    Aggregate(sig2 SignatureBytes) SignatureBytes

    // VerifyAggregate verifies the aggregate signature with the aggregate
    // public key over all the distinct messages given. Note that if all
    // messages are the same, it is sufficient and correct to only call
    // `Verify` since it is a subset of `VerifyAggregate`.
    VerifyAggregate(messages [Message], aggPk BLSPublicKey, aggSig SignatureBytes) bool
}

// BLSPublicKey is a PublicKey with an addition method to aggregate public keys
// together.
type BLSPublicKey struct {
    PublicKey

    // Aggregate this public key with p2 into one public key. This aggregated
    // public key can 
    // - verify aggregated signatures signed by the two BLSPublicKey
    // - be aggregated further down with other (aggregated or not) BLSPublicKey.
    Aggregate(p2 BLSPublicKey) BLSPublicKey
}
package crypto

import util "github.com/filecoin-project/specs/util"

func (self *BLS_I) Verify(input util.Bytes, pk PublicKey, sig util.Bytes) bool {
	// blsPk := pk.(*BLSPublicKey)
	// 1. Verify public key according to string_to_curve section 2.6.2.1. in
	// 	https://tools.ietf.org/html/draft-boneh-bls-signature-00#page-12
	// 2. Verify signature according to section 2.3
	// 	https://tools.ietf.org/html/draft-boneh-bls-signature-00#page-8
	panic("bls.Verify TODO")
	return false
}

func (self *BLS_I) MaxSigValue() util.Bytes {
	panic("TODO")
}

func (self *BLS_I) Sign(input util.Bytes, sk *SecretKey) bool {
	panic("see 2.3 in https://tools.ietf.org/html/draft-boneh-bls-signature-00#page-8")
	return false
}

func (self *BLS_I) Aggregate(sig2 util.Bytes) util.Bytes {
	panic("see 2.5 in https://tools.ietf.org/html/draft-boneh-bls-signature-00#page-8")
	var ret util.Bytes
	return ret
}

func (self *BLS_I) VerifyAggregate(messages []util.Bytes, aggPk PublicKey, aggSig util.Bytes) bool {
	panic("see 2.5.2 in https://tools.ietf.org/html/draft-boneh-bls-signature-00#page-9")
	return false
}

Choice of group: The BLS signature requires the use of a pairing-equipped curve which generally yield three groups: G_1, G_2 and G_T. In the BLS signature scheme, there is a choice on which group to define the public key and the signature:

  • Public key is on G_1 and signature on G_2
  • Public key is on G_2 and signature on G_1

The group G_1 is “smaller” and hence offer faster arithmetic operations and smaller byte representation of its elements. Filecoin currently uses the group G_1 for representing public keys and the group G_2 for representing signatures.

Wire Format: Filecoin uses the standard way to serialize BLS signatures as explained in the RFC Section 2.6.1.

Rationale: BLS signatures have two main characteristics that are making them ideal candidates in recent blockchain systems:

  • BLS signatures are deterministic: for a given message and a given secret key, the signature is always the same. That feature removes an important security weakness of most randomized signature schemes: signer must never re-use the same randomness twice otherwise this reveals its private key. As well, deterministic signatures are an ideal candidate to reduce the attack surface in terms of grinding, which is a real concern in recent proof of stake systems.
  • BLS signatures are aggregatable: one can aggregate signatures from different signers into one single signature. This feature enables drastically saving space on the blockchain, especially when aggregating user transactions.

Aggregation Functionality: The aggregation functionality is commutative and associative, enabling partial aggregation. For example, given (PK1, sig1), (PK2, sig2), (PK3, sig3), one can first aggregate (PK12 = PK1 + PK2, sig12 = sig1 + sig2) then aggregate with the third tuple to produce (PK123 = PK12 + PK3, sig123 = sig12 + sig3).

Aggregation Security: The naive BLS signature aggregation scheme is vulnerable to rogue-key attacks where the attacker can freely choose its public key. To prevent against this class of attacks there exists three different kind of measures, as explained here:

  • Enforce distinct messages
  • Prove knowledge of the secret key
  • Use a modified scheme (such as BLS Multi Sig)

Fortunately, Filecoin can enforce the second condition to safely use the aggregation property: Filecoin uses aggregation only for aggregating the transaction’s signature of a block. Since Filecoin uses the account model to represent the state of the chain, each message for a given signer is used in combination with a nonce to avoid replay attacks. As a direct consequence, every transaction’s message is unique thereby the aggregation is done on distinct messages. Obviously, the assumption here is that the block producer enforces that distinction and the other miners will check all transactions to make sure they are valid. The validity of a transaction in Filecoin’s context implies that the signature is correctly formed over the message with the correct nonce.