Skip to content

Commit

Permalink
Fouque tibouchi hashing to g1 and g2, test vectors, and spec (Chia-Ne…
Browse files Browse the repository at this point in the history
…twork#19)

* Implement fouque tibouchi according to rust, but with sha256

* Implement FT hashing in c, and update test vectors

* comments, reorganize, constant time algorithm

* Hardcode constants, and faster cofactor multiplication

* Fix hashing to g1

* Add spec

* Uncomment tests

* Remove unnecessary check in verify

* More progress on spec

* Add signature division to spec

* tweaks to spec and minor fixes
  • Loading branch information
mariano54 authored Sep 20, 2018
1 parent 98323d0 commit fe8c3bc
Show file tree
Hide file tree
Showing 22 changed files with 1,010 additions and 276 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ ENDIF()
set(STBIN "OFF" CACHE STRING "")

set(FP_METHD "INTEG;INTEG;INTEG;MONTY;LOWER;SLIDE" CACHE STRING "")
set(COMP "-O3 -funroll-loops -fomit-frame-pointer -finline-small-functions -march=native -mtune=native" CACHE STRING "")
set(COMP "-O3 -funroll-loops -fomit-frame-pointer -march=native -mtune=native" CACHE STRING "")
set(FP_PMERS "off" CACHE STRING "")
set(FPX_METHD "INTEG;INTEG;LAZYR" CACHE STRING "")
set(EP_PLAIN "off" CACHE STRING "")
Expand Down
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ NOTE: THIS LIBRARY IS A DRAFT AND NOT YET REVIEWED FOR SECURITY

Implements BLS signatures with aggregation as in [Boneh, Drijvers, Neven 2018](https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html), using
[relic toolkit](https://github.com/relic-toolkit/relic) for cryptographic primitives (pairings, EC, hashing).
The [BLS12-381](https://github.com/zkcrypto/pairing/tree/master/src/bls12_381) curve is used.
The [BLS12-381](https://github.com/zkcrypto/pairing/tree/master/src/bls12_381) curve is used. The spec is [here](https://github.com/Chia-Network/bls-signatures/tree/master/SPEC.md).

Features:
* Non-interactive signature aggregation on identical or distinct messages
Expand Down Expand Up @@ -222,19 +222,18 @@ ude -Ibls-signatures/src/ -L./bls-signatures/build/ -l bls yourfile.cpp
```

### Notes on dependencies
Changes performed to relic: Added config files for Chia, and added gmp include in relic.h.
Allow passing in hash to ep2_map. Custom inversion function. Note: relic is an LGPL 2.1 dependency.
Changes performed to relic: Added config files for Chia, and added gmp include in relic.h, new ep_map and ep2_map. Custom inversion function. Note: relic is an LGPL 2.1 dependency.

Libsodium and GMP are optional dependencies: libsodium gives secure memory allocation,
and GMP speeds up the library by ~ 3x. To install them, unzip the directories in contrib,
and follow the instructions for each repo.
and GMP speeds up the library by ~ 3x. To install them, either download them from github
and follow the instructions for each repo, or use a package manager like APT or brew.

### Discussion
Discussion about this library and other Chia related development is on Keybase.
Install Keybase, and run the following to join the Chia public channels:

```bash
keybase team request-access chia_network
keybase team request-access chia_network.public
```

### Code style
Expand All @@ -248,7 +247,6 @@ keybase team request-access chia_network

### TODO
* Serialize aggregation info
* New constant time hashing to g2
* Secure allocation during signing, key derivation
* Threshold signatures
* Remove unnecessary dependency files
Expand All @@ -257,5 +255,5 @@ keybase team request-access chia_network
* More tests vectors (failed verifications, etc)


### Test vectors
Test vectors can be found in src/test.cpp and python-impl/tests.py.
### Specification and test vectors
The specification and test vectors can be found [here](https://github.com/Chia-Network/bls-signatures/tree/master/SPEC.md). Test vectors can also be seen in the python or cpp test files.
310 changes: 310 additions & 0 deletions SPEC.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,310 @@
# BLS Signature Scheme Specificiation

This document describes an instantiation of the BLS Signature Scheme as it will be used in the Chia blockchain. It still a draft and subject to change.

The curve used is BLS12-381 as described [here](https://github.com/ebfull/pairing/tree/master/src/bls12_381). This spec is based off of zkcrypto/pairing spec, described in that link. The aggregation used is based on [Boneh, Drijvers, Neven](https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html).

Multiplicative notation is used for all groups. G1 is used for public keys, and G2 is used for signatures. There are several other small differences with the rust pairing spec.

### Parameters
x = -0xd201000000010000

q = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab

E: y^2 = x^3 + 4 (G1 is over this curve)

E': y^2 = x^3 + 4(i+1) (G2 is over this curve)

gx = (0x17F1D3A73197D7942695638C4FA9AC0FC3688C4F9774B905A14E3A3F171BAC586C55E83FF97A1AEFFB3AF00ADB22C6BB)

gy = (0x08B3F481E3AAA0F1A09E30ED741D8AE4FCF5E095D5D00AF600DB18CB2C04B3EDD03CC744A2888AE40CAA232946C5E7E1)

g2x = (0x24aa2b2f08f0a91260805272dc51051c6e47ad4fa403b02b4510b647ae3d1770bac0326a805bbefd48056c8c121bdb8, 0x13e02b6052719f607dacd3a088274f65596bd0d09920b61ab5da61bbdc7f5049334cf11213945d57e5ac7d055d042b7e)

g2y =
(0xce5d527727d6e118cc9cdc6da2e351aadfd9baa8cbdd3a76d429a695160d12c923ac9cc3baca289e193548608b82801, 0x606c4a02ea734cc32acd2b02bc28b99cb3e287e85a763af267492ab572e99ab3f370d275cec1da1aaa9075ff05f79be)

r = n = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001

h = 0x396C8C005555E1568C00AAAB0000AAAB

k = 12

### Subroutines

#### pairing operation e
* input: G<sub>1</sub> element p, G<sub>2</sub> element q
* output: Fq12 element

Performs an ate pairing between p and q.

#### swEncode
* input: Fq element
* output: G<sub>1</sub> or G<sub>2</sub> element

Shallue and van de Woestijne encoding of a field element to an ec point. Used for Fouque-Tibouchi hashing: https://www.di.ens.fr/~fouque/pub/latincrypt12.pdf. 0 maps to the point at infinity.


#### psi
* input: G<sub>1</sub> element p
* output: G<sub>2</sub> element p2
```python
# Twist is map from E -> E'
# Untwist is map From E' -> E
# Described in page 11 of "Efficient hash maps to G2 on BLS curves" by Budroni and Pintore.
return twist(qPowerFrobenius(untwist(p)))
```

#### hash256
* input: message m
* output: 256 bit bytearray
```python
return SHA256(m)
```

#### hash512
* input: message m
* output: 512 bit bytearray
```python
# 0 or 1 bytes are appended to each input
return hash256(m + 0) + hash256(m + 1)
```

#### hashG<sub>1</sub>
* input: message m
* output: G<sub>1</sub> element
```python
h <- hash256(m)
t0 <- hash512(h + b"G1_0") % q
t1 <- hash512(h + b"G1_1") % q

p <- swEncode(t0) * swEncode(t1)

# Map to the r-torsion by raising to cofactor power
return p ^ h
```
#### hashG<sub>2</sub>
* input: message m
* output: G<sub>2</sub> element
```python
h <- hash256(m)
t00 <- hash512(h + b"G2_0_c0") % q
t01 <- hash512(h + b"G2_0_c1") % q
t10 <- hash512(h + b"G2_1_c0") % q
t11 <- hash512(h + b"G2_1_c1") % q

t0 <- Fq2(t00, t01)
t1 <- Fq2(t10, t11)

p <- swEncode(t0) * swEncode(t1)

# Map to the r-torsion by raising to cofactor power
# Described in page 11 of "Efficient hash maps to G2 on BLS curves" by Budroni and Pintore.
x <- abs(x)
return p ^ (x^2 + x - 1) - psi(p ^ (x + 1)) + psi(psi(p ^ 2))
```

#### hashPks
* input: G<sub>1</sub> elements pks, number of outputs m
* output: n 256bit integers T
```python
pkHash <- hash256(pk.serialize() for pk in pks)
return [(hash256(fourBytes(i) + pkHash) % n) for i in range(m)]
```

### Methods
#### keyGen
* input: random seed s
* output: field element in Z<sub>q</sub>, G<sub>1</sub> element
```python
# Perform an HMAC using hash256, and the following string as the key
sk <- hmac256(s, b"BLS private key seed") mod n
pk <- g1 ^ sk
```

#### sign
* input: bytes m, Z<sub>q</sub> element sk
* output: G<sub>2</sub> element σ
```python
σ <- hashG2(m) ^ sk
```

#### verify
* input:
* G<sub>2</sub> element σ
* map((bytes m, G<sub>1</sub> pk) -> Z<sub>n</sub> exponent) aggInfo
* output: bool
```python
if aggInfo is empty: return false
pks = []
ms = []
for each distinct messsageHash m in aggInfo:
pkAgg <- 1
for each pk grouped with m:
pkAgg *= pk ^ aggInfo[(m, pk)]
pks.add(pkAgg)
ms.add(m)
return 1 == e(g1 ^ (q-1), σ) * prod e(pks[i], ms[i])
```

#### aggregate
* input:
* list of G<sub>2</sub> elements: signatures
* list of map((bytes m, G<sub>1</sub> pk) -> Z<sub>n</sub> exponent) aggInfo
* output:
* G<sub>2</sub> element σ<sub>agg</sub>
* map((bytes m, G<sub>1</sub> pk) -> Z<sub>n</sub> exponent) newAggInfo
```python
messageHashes <- [i.messageHashes for i in aggInfo]
publicKeys <- [i.pks for i in aggInfo]

collidingMessages <- messages that appear in more than one messageHashes list

if colidingMessages is empty:
sigAgg <- product([sig for sig in signatures])
newAggInfo <- mergeInfos(aggInfo)
return sigAgg, newAggInfo

collidingSigs <- signatures that contain collidingMessages
nonCollidingSigs <- remaining signatures
sortKeys <- all (message, publicKey) pairs in colliding groups

sort(sort_keys) # Sort first by message, then by pk
sortedPublicKeys <- [k[1] for k in sortKeys]
Ts <- hashPks(sortedPublicKeys, len(collidingSigs))

sigAgg <- product(collidingSigs[i] ^ Ts[i] for i in collidingSigs) * product(sig for sig in nonCollidingSigs)
newAggInfo = mergeInfos(aggInfos)
return (sigAgg, newAggInfo)
```

#### divide
* input:
* Divident signature: dividendSig
* map((bytes m, G<sub>1</sub> pk) -> Z<sub>n</sub> exponent) dividendAggInfo
* Divisor signatures: divisorSigs
* list of map((bytes m, G<sub>1</sub> pk) -> Z<sub>n</sub> exponent) divisorsAggInfo
* output:
* G<sub>2</sub> element σ<sub>agg</sub>
* map((bytes m, G<sub>1</sub> pk) -> Z<sub>n</sub> exponent) newAggInfo
```python
messageHashesToRemove <- []
pubKeysToRemove <- []
prod <- 1 # Point at infinity
for divisorSig, i in enumerate(divisorSigs):
for j in range(len(divisorsAggInfo[i].keys)):
divisor <- divisorsAggInfo[i][j]
assert(j in dividendAggInfo[i])
dividend <- dividendAggInfo[i][j]
if j == 0:
quotient <- divided / divisor in Fq
else:
assert((divided / divisor in Fq) == quotient)
messageHashesToRemove.append(divisorSig.messageHashes[j])
pubKeysToRemove.append(divisorSig.pubkeys[j])
prod <- prod * -divisorSig
aggSig <- dividendSig * prod
newAggInfo <- dividendAggInfo
newAggInfo.remove((messageHashesToRemove[i], pubKeysToRemove[i] for i in range(len(messageHashesToRemove)))
return (aggSig, newAggInfo)
```

### Serialization
**private key (32 bytes):** Big endian integer.

**pubkey (48 bytes):** 381 bit affine x coordinate, encoded into 48 big-endian bytes. Since we have 3 bits left over in the beginning, the first bit is set to 1 iff y coordinate is the lexicographically largest of the two valid ys. The public key fingerprint is the first 4 bytes of hash256(serialize(pubkey)).

**signature (96 bytes):** Two 381 bit integers (affine x coordinate), encoded into two 48 big-endian byte arrays. Since we have 3 bits left over in the beginning, the first bit is set to 1 iff the y coordinate is the lexicographically largest of the two valid ys. (The term with the i is compared first, i.e 3i + 1 > 2i + 7).


### HD keys
HD keys will follow Bitcoin's BIP32 specification, with the following differences:
* The HMAC key to generate a master private key used is not "Bitcoin seed" it is "BLS HD seed".
* The master secret key is generated mod n from the master seed,
since not all 32 byte sequences are valid BLS private keys
* Instead of SHA512(input), do hash512(input) as defined above.
* Mod n for the output of key derivation.
* ID of a key is hash256(pk) instead of HASH160(pk)
* Serialization of extended public key is 93 bytes, since BLS public keys are longer

### Test vectors
#### Signatures
* keygen([1,2,3,4,5])
* sk1: 0x022fb42c08c12de3a6af053880199806532e79515f94e83461612101f9412f9e
* pk1 fingerprint: 0x26d53247
* keygen([1,2,3,4,5,6])
* pk2 fingerprint: 0x289bb56e
* sign([7,8,9], sk1)
* sig1: 0x93eb2e1cb5efcfb31f2c08b235e8203a67265bc6a13d9f0ab77727293b74a357ff0459ac210dc851fcb8a60cb7d393a419915cfcf83908ddbeac32039aaa3e8fea82efcb3ba4f740f20c76df5e97109b57370ae32d9b70d256a98942e5806065
* sign([7,8,9], sk2)
* sig2: 0x975b5daa64b915be19b5ac6d47bc1c2fc832d2fb8ca3e95c4805d8216f95cf2bdbb36cc23645f52040e381550727db420b523b57d494959e0e8c0c6060c46cf173872897f14d43b2ac2aec52fc7b46c02c5699ff7a10beba24d3ced4e89c821e
* verify(sig1, AggregationInfo(pk1, [7,8,9]))
* true
* verify(sig2, AggregationInfo(pk2, [7,8,9]))
* true

#### Aggregation
* aggregate([sig1, sig2])
* aggSig: 0x975b5daa64b915be19b5ac6d47bc1c2fc832d2fb8ca3e95c4805d8216f95cf2bdbb36cc23645f52040e381550727db420b523b57d494959e0e8c0c6060c46cf173872897f14d43b2ac2aec52fc7b46c02c5699ff7a10beba24d3ced4e89c821e
* verify(aggSig2, mergeInfos(sig1.aggInfo, sig2.aggInfo))
* true
* verify(sig1, AggregationInfo(pk2, [7,8,9]))
* false
* sig3 = sign([1,2,3], sk1)
* sig4 = sign([1,2,3,4], sk1)
* sig5 = sign([1,2], sk2)
* aggregate([sig3, sig4, sig5])
* aggSig2: 0x8b11daf73cd05f2fe27809b74a7b4c65b1bb79cc1066bdf839d96b97e073c1a635d2ec048e0801b4a208118fdbbb63a516bab8755cc8d850862eeaa099540cd83621ff9db97b4ada857ef54c50715486217bd2ecb4517e05ab49380c041e159b
* verify(aggSig2, mergeInfos(sig3.aggInfo, sig4.aggInfo, sig5.aggInfo))
* true
* sig1 = sk1.sign([1,2,3,40])
* sig2 = sk2.sign([5,6,70,201])
* sig3 = sk2.sign([1,2,3,40])
* sig4 = sk1.sign([9,10,11,12,13])
* sig5 = sk1.sign([1,2,3,40])
* sig6 = sk1.sign([15,63,244,92,0,1])
* sigL = aggregate([sig1, sig2])
* sigR = aggregate([sig3, sig4, sig5])
* verify(sigL)
* true
* verify(sigR)
* true
* aggregate([sigL, sigR, sig6])
* sigFinal: 0x07969958fbf82e65bd13ba0749990764cac81cf10d923af9fdd2723f1e3910c3fdb874a67f9d511bb7e4920f8c01232b12e2fb5e64a7c2d177a475dab5c3729ca1f580301ccdef809c57a8846890265d195b694fa414a2a3aa55c32837fddd80
* verify(sigFinal)
* true

#### Signature division
* divide(sigFinal, [sig2, sig5, sig6])
* quotient: 0x8ebc8a73a2291e689ce51769ff87e517be6089fd0627b2ce3cd2f0ee1ce134b39c4da40928954175014e9bbe623d845d0bdba8bfd2a85af9507ddf145579480132b676f027381314d983a63842fcc7bf5c8c088461e3ebb04dcf86b431d6238f
* verify(quotient)
* true
* divide(quotient, [sig6])
* throws due to not subset
* divide(sigFinal, [sig1])
* does not throw
* divide(sig_final, [sigL])
* throws due to not unique
* sig7 = sign([9,10,11,12,13], sk2)
* sig8 = sign([15,63,244,92,0,1], sk2)
* sigFinal2 = aggregate([sigFinal, aggregate([sig7, sig8])])
* divide(sigFinal2, aggregate([sig7, sig8]))
* quotient2: 0x06af6930bd06838f2e4b00b62911fb290245cce503ccf5bfc2901459897731dd08fc4c56dbde75a11677ccfbfa61ab8b14735fddc66a02b7aeebb54ab9a41488f89f641d83d4515c4dd20dfcf28cbbccb1472c327f0780be3a90c005c58a47d3
* verify(quotient2)
* true

#### HD keys
* esk = ExtendedPrivateKey([1, 50, 6, 244, 24, 199, 1, 25])
* esk.publicKeyFigerprint
* 0xa4700b27
* esk.chainCode
* 0xd8b12555b4cc5578951e4a7c80031e22019cc0dce168b3ed88115311b8feb1e3
* esk77 = esk.privateChild(77 + 2^31)
* esk77.publicKeyFingerprint
* 0xa8063dcf
* esk77.chainCode
* 0xf2c8e4269bb3e54f8179a5c6976d92ca14c3260dd729981e9d15f53049fd698b
* esk.privateChild(3).privateChild(17).publicKeyFingerprint
* 0xff26a31f
* esk.extendedPublicKey.publicChild(3).publicChild(17).publicKeyFingerprint
* 0xff26a31f
4 changes: 4 additions & 0 deletions contrib/relic/include/relic_core.h
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,10 @@ typedef struct _ctx_t {
bn_st ep2_r;
/** The cofactor of the group order in the elliptic curve. */
bn_st ep2_h;
/** sqrt(-3) in the field for this curve */
bn_st ep2_s3;
/** (sqrt(-3) - 1) / 2 in the field for this curve */
bn_st ep2_s32;
/** Flag that stores if the prime curve is a twist. */
int ep2_is_twist;
#ifdef EP_PRECO
Expand Down
5 changes: 4 additions & 1 deletion contrib/relic/include/relic_ep.h
Original file line number Diff line number Diff line change
Expand Up @@ -1083,7 +1083,10 @@ void ep_norm(ep_t r, const ep_t p);
void ep_norm_sim(ep_t *r, const ep_t *t, int n);

/**
* Maps a byte array to a point in a prime elliptic curve.
* Maps a byte array to a point in a prime elliptic curve. The
* algorithm implemented is the Fouque-Tibouchi algorithm from the
* paper "Indifferentiable Hashing to Barreto-Naehrig curves" for
* the BLS12-381 curve.
*
* @param[out] p - the result.
* @param[in] msg - the byte array to map.
Expand Down
Loading

0 comments on commit fe8c3bc

Please sign in to comment.