Priv to pub blst (#69)

* Add test vector to detect priv_to_pub bugs

* Bug "narrowed" down to hesenbug that disappears when using fsanitize=address

* Fix -d:release incompat with BLST

* change privToPub comment

* actually only GCC is affected, not Clang
This commit is contained in:
Mamy Ratsimbazafy 2020-08-12 15:58:46 +02:00 committed by GitHub
parent ecfb3b6219
commit 7e5dfb906d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 241 additions and 28 deletions

3
.gitignore vendored
View File

@ -9,3 +9,6 @@ build/
*.la
*.exe
*.dll
# Sage
*.sage.py

1
amcl

@ -1 +0,0 @@
Subproject commit fa0a45a35dfb0e6d16b5d4f9e0bca19b5460fe4b

View File

@ -15,6 +15,8 @@ proc test(env, path: string, lang = "c") =
mkDir "build"
exec "nim " & lang & " " & env &
" --outdir:build -r --hints:off --warnings:off " & path
exec "nim " & lang & " -d:release " & env &
" --outdir:build -r --hints:off --warnings:off " & path
### tasks
task test, "Run all tests":
@ -30,10 +32,13 @@ task test, "Run all tests":
test "-d:BLS_BACKEND=miracl", "tests/eth2_vectors.nim"
# key Derivation - EIP 2333
test "-d:BLS_BACKEND=miracl", "tests/eip2333_key_derivation.nim"
# Secret key to pubkey
test "-d:BLS_BACKEND=miracl", "tests/priv_to_pub.nim"
when sizeof(int) == 8 and (defined(arm64) or defined(amd64)):
test "-d:BLS_BACKEND=blst", "tests/eth2_vectors.nim"
test "-d:BLS_BACKEND=blst", "tests/eip2333_key_derivation.nim"
test "-d:BLS_BACKEND=blst", "tests/priv_to_pub.nim"
# # Ensure benchmarks stay relevant. Ignore Windows 32-bit at the moment
# if not defined(windows) or not existsEnv"PLATFORM" or getEnv"PLATFORM" == "x64":

View File

@ -93,6 +93,30 @@ func `==`*(a, b: PublicKey or Signature or ProofOfPossession): bool {.inline.} =
# ----------------------------------------------------------------------
# Serialization / Deserialization
func toHex*(
obj: SecretKey|PublicKey|Signature|ProofOfPossession|AggregateSignature,
): string =
## Return the hex representation of a BLS signature scheme object
## They are serialized in compressed form
when obj is SecretKey:
const size = 32
var bytes{.noInit.}: array[size, byte]
bytes.blst_bendian_from_scalar(obj.scalar)
elif obj is PublicKey:
const size = 48
var bytes{.noInit.}: array[size, byte]
bytes.blst_p1_affine_compress(obj.point)
elif obj is (Signature or ProofOfPossession):
const size = 96
var bytes{.noInit.}: array[size, byte]
bytes.blst_p2_affine_compress(obj.point)
elif obj is AggregateSignature:
const size = 96
var bytes{.noInit.}: array[size, byte]
bytes.blst_p2_compress(obj.point)
result = bytes.toHex()
func fromBytes*(
obj: var (Signature|ProofOfPossession),
raw: openarray[byte] or array[96, byte]
@ -100,7 +124,6 @@ func fromBytes*(
## Initialize a BLS signature scheme object from
## its raw bytes representation.
## Returns true on success and false otherwise
# TODO: consider using affine coordinates everywhere beside
const L = 96
when raw is array:
result = obj.point.blst_p2_uncompress(raw) == BLST_SUCCESS
@ -164,30 +187,6 @@ func fromHex*(
except:
return false
func toHex*(
obj: SecretKey|PublicKey|Signature|ProofOfPossession|AggregateSignature,
): string =
## Return the hex representation of a BLS signature scheme object
## They are serialized in compressed form
when obj is SecretKey:
const size = 32
var bytes{.noInit.}: array[size, byte]
bytes.blst_bendian_from_scalar(obj.scalar)
elif obj is PublicKey:
const size = 48
var bytes{.noInit.}: array[size, byte]
bytes.blst_p1_affine_compress(obj.point)
elif obj is (Signature or ProofOfPossession):
const size = 96
var bytes{.noInit.}: array[size, byte]
bytes.blst_p2_affine_compress(obj.point)
elif obj is AggregateSignature:
const size = 96
var bytes{.noInit.}: array[size, byte]
bytes.blst_p2_compress(obj.point)
result = bytes.toHex()
func serialize*(
dst: var array[32, byte],
obj: SecretKey): bool {.inline.} =
@ -232,8 +231,10 @@ func exportRaw*(signature: Signature): array[96, byte] {.inline.}=
func privToPub*(secretKey: SecretKey): PublicKey {.inline.} =
## Generates a public key from a secret key
# TODO, small secret keys like "1000" are not properly
# computed, those are used in test suites
## Generates a public key from a secret key
## This requires some -O3 compiler optimizations to be off
## as such {.passC: "-fno-peel-loops -fno-tree-loop-vectorize".}
## is automatically added to the compiler flags
var pk {.noInit.}: blst_p1
pk.blst_sk_to_pk_in_g1(secretKey.scalar)
result.point.blst_p1_to_affine(pk)

View File

@ -9,6 +9,11 @@
import std/os
when defined(gcc):
# Using this option will miscompile
# scalar multiplication. Clang works fine.
{.passC: "-fno-tree-loop-vectorize".}
{.compile: ".."/".."/"vendor"/"blst"/"build"/"assembly.S".}
{.compile: ".."/".."/"vendor"/"blst"/"src"/"server.c".}

76
tests/priv_to_pub.nim Normal file
View File

@ -0,0 +1,76 @@
# Nim-BLSCurve
# Copyright (c) 2018-Present Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
import ../blscurve
# This test ensures that fake secret keys can be used for testing
# In particular this caught compiler options that miscompile BLST,
# namely -fpeel-loops -ftree-loop-vectorize
# which are unfortunately enabled at -O3
proc test_sk_to_pk(seckey, pubkey: string) =
var sk{.noInit.}: SecretKey
let ok = sk.fromHex(seckey)
doAssert ok
let pk = sk.privToPub()
doAssert pk.toHex() == pubkey, "\ncomputed: " & pk.toHex() & "\nexpected: " & pubkey & '\n'
echo "SUCCESS"
test_sk_to_pk(
seckey = "00000000000000000000000000000000000000000000000000000000000003e8",
pubkey = "a60e75190e62b6a54142d147289a735c4ce11a9d997543da539a3db57def5ed83ba40b74e55065f02b35aa1d504c404b"
)
test_sk_to_pk(
seckey = "00000000000000000000000000000000000000000000000000000000000003e9",
pubkey = "ae12039459c60491672b6a6282355d8765ba6272387fb91a3e9604fa2a81450cf16b870bb446fc3a3e0a187fff6f8945"
)
test_sk_to_pk(
seckey = "00000000000000000000000000000000000000000000000000000000000003ea",
pubkey = "947b327c8a15b39634a426af70c062b50632a744eddd41b5a4686414ef4cd9746bb11d0a53c6c2ff21bbcf331e07ac92"
)
test_sk_to_pk(
seckey = "00000000000000000000000000000000000000000000000000000000000003eb",
pubkey = "85fc4ae543ca162474586e76d72c47d0151c3cb7b77e82c87e554abf72548e2e746bc675805b688b5016269e18ff4250"
)
test_sk_to_pk(
seckey = "00000000000000000000000000000000000000000000000000000000000003ec",
pubkey = "8caa0de862793e567c6050aa822db2d6cb2b520bc62b6dbcba7e773067ed09c7ba0282d7c20e01500c6c2fa76408aded"
)
# From BLST Rust test
# cargo test test_sign -- --show-output
#
# [test]
# fn test_sign() {
# let ikm: [u8; 32] = [
# 0x93, 0xad, 0x7e, 0x65, 0xde, 0xad, 0x05, 0x2a, 0x08, 0x3a,
# 0x91, 0x0c, 0x8b, 0x72, 0x85, 0x91, 0x46, 0x4c, 0xca, 0x56,
# 0x60, 0x5b, 0xb0, 0x56, 0xed, 0xfe, 0x2b, 0x60, 0xa6, 0x3c,
# 0x48, 0x99,
# ];
#
# let sk = SecretKey::key_gen(&ikm, &[]).unwrap();
# print_bytes(&sk.serialize(), "sk: ");
# let pk = sk.sk_to_pk();
# print_bytes(&pk.compress(), "pk: ");
#
# ---- min_pk::tests::test_sign stdout ----
# sk: 47faea55fe00a78306449165c017c9db86411a4c2467b4b89e21323c746406a0
# pk: a18e29d0185a5a6d19edf052ae098fd2924f579b6dfb4905332b8f4fc78adeb3188ad8315bf279a144be026ac08f3441
test_sk_to_pk(
seckey = "47faea55fe00a78306449165c017c9db86411a4c2467b4b89e21323c746406a0",
pubkey = "a18e29d0185a5a6d19edf052ae098fd2924f579b6dfb4905332b8f4fc78adeb3188ad8315bf279a144be026ac08f3441"
)

124
tests/priv_to_pub.sage Normal file
View File

@ -0,0 +1,124 @@
# Nim-BLSCurve
# Copyright (c) 2018-Present Status Research & Development GmbH
# Licensed under either of
# * Apache License, version 2.0, ([LICENSE-APACHE](LICENSE-APACHE))
# * MIT license ([LICENSE-MIT](LICENSE-MIT))
# at your option.
# This file may not be copied, modified, or distributed except according to
# those terms.
# Test case generator
# ------------------------------------------------------------------------------
# Parameters
# ------------------------------------------------------------------------------
x = -(2^63 + 2^62 + 2^60 + 2^57 + 2^48 + 2^16)
p = (x - 1)^2 * (x^4 - x^2 + 1)//3 + x
r = x^4 - x^2 + 1
cofactor = Integer('0x396c8c005555e1568c00aaab0000aaab')
# Effective cofactor for the G2 curve (that leads to equivalent hashToG2 when using endomorphisms)
g2_h_eff = Integer('0xbc69f08f2ee75b3584c6a0ea91b352888e2a8e9145ad7689986ff031508ffe1329c2f178731db956d82bf015d1212b02ec0ec69d7477c1ae954cbc06689f6a359894c0adebbf6b4e8020005aaa95551')
# Finite fields
Fp = GF(p)
K2.<u> = PolynomialRing(Fp)
Fp2.<beta> = Fp.extension(u^2+1)
# K6.<v> = PolynomialRing(Fp2)
# Fp6.<eta> = Fp2.extension(v^3-Fp2([1, 1])
# K12.<w> = PolynomialRing(Fp6)
# K12.<gamma> = F6.extension(w^2-eta)
# Curves
b = 4
SNR = Fp2([1, 1])
G1 = EllipticCurve(Fp, [0, b])
G2 = EllipticCurve(Fp2, [0, b*SNR])
# Generator points
if False:
P1 = G1.gen(0)
(P1x, P1y, P1z) = P1
print('P1x: ' + Integer(P1x).hex())
print('P1y: ' + Integer(P1y).hex())
else:
# https://tools.ietf.org/html/draft-irtf-cfrg-pairing-friendly-curves-02#section-4.3.2
P1 = G1(
Integer('0x17f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb'),
Integer('0x08b3f481e3aaa0f1a09e30ed741d8ae4fcf5e095d5d00af600db18cb2c04b3edd03cc744a2888ae40caa232946c5e7e1'),
Integer(1)
)
def priv_to_pub(scalar):
return scalar * P1
def pointToString(P):
(Px, Py, Pz) = P
return '(x: ' + Integer(Px).hex() + ', y: ' + Integer(Py).hex() + ')'
def pointToCompressed(P):
(Px, Py, Pz) = P
rawX = Integer(Px)
if Py > p - Py:
rawX |= 1 << 381
rawX |= 1 << 383
return int(rawX).to_bytes(48, 'big').hex()
for i in range(1000, 1010):
print('---------------------------------------')
print(f'seckey: {i}')
print(f'seckey hex: {i.to_bytes(32, "big").hex()}')
pubkey = priv_to_pub(i)
print(f'pubKey (uncompressed): {pointToString(pubkey)}')
print(f'pubKey (compressed): {pointToCompressed(pubkey)}')
# ---------------------------------------
# seckey: 1000
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003e8
# pubKey (uncompressed): (x: 60e75190e62b6a54142d147289a735c4ce11a9d997543da539a3db57def5ed83ba40b74e55065f02b35aa1d504c404b, y: 17ecb08d4bb31b7eeb6581e6808c6abf58958845b917e085baaab098b9a8a3ecc8caf6f1a06c46b0f7812b09aa52e7a0)
# pubKey (compressed): a60e75190e62b6a54142d147289a735c4ce11a9d997543da539a3db57def5ed83ba40b74e55065f02b35aa1d504c404b
# ---------------------------------------
# seckey: 1001
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003e9
# pubKey (uncompressed): (x: e12039459c60491672b6a6282355d8765ba6272387fb91a3e9604fa2a81450cf16b870bb446fc3a3e0a187fff6f8945, y: 18b6c1ed9f45d3cbc0b01b9d038dcecacbd702eb26469a0eb3905bd421461712f67f782b4735849644c1772c93fe3d09)
# pubKey (compressed): ae12039459c60491672b6a6282355d8765ba6272387fb91a3e9604fa2a81450cf16b870bb446fc3a3e0a187fff6f8945
# ---------------------------------------
# seckey: 1002
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003ea
# pubKey (uncompressed): (x: 147b327c8a15b39634a426af70c062b50632a744eddd41b5a4686414ef4cd9746bb11d0a53c6c2ff21bbcf331e07ac92, y: 78c2e9782fa5d9ab4e728684382717aa2b8fad61b5f5e7cf3baa0bc9465f57342bb7c6d7b232e70eebcdbf70f903a45)
# pubKey (compressed): 947b327c8a15b39634a426af70c062b50632a744eddd41b5a4686414ef4cd9746bb11d0a53c6c2ff21bbcf331e07ac92
# ---------------------------------------
# seckey: 1003
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003eb
# pubKey (uncompressed): (x: 5fc4ae543ca162474586e76d72c47d0151c3cb7b77e82c87e554abf72548e2e746bc675805b688b5016269e18ff4250, y: 7c13f661fd28bf1ea1cf51c762dda21547877eedf54e9263b3b5d0923820b58ed81503beb24fc4cd50bd47d9d67d7e)
# pubKey (compressed): 85fc4ae543ca162474586e76d72c47d0151c3cb7b77e82c87e554abf72548e2e746bc675805b688b5016269e18ff4250
# ---------------------------------------
# seckey: 1004
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003ec
# pubKey (uncompressed): (x: caa0de862793e567c6050aa822db2d6cb2b520bc62b6dbcba7e773067ed09c7ba0282d7c20e01500c6c2fa76408aded, y: c7c359be46db8efd81618b29cea252fdbfff8229dd3e3c7f98c10801fdc9bb65403d124b43a934f8a1cf8ca351ee1df)
# pubKey (compressed): 8caa0de862793e567c6050aa822db2d6cb2b520bc62b6dbcba7e773067ed09c7ba0282d7c20e01500c6c2fa76408aded
# ---------------------------------------
# seckey: 1005
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003ed
# pubKey (uncompressed): (x: a273fd05323e1381e10e93e683c34647328127020b3507fc8cddc337038e33fbd7a99ef0d2c7b6a278d7f8116162560, y: 134e59e38d0cdda7464634c997d9f08b7e336bdfa895b764f8c4e24e52e3f46683d8e798ada2d65f055adb4a7bf6c279)
# pubKey (compressed): aa273fd05323e1381e10e93e683c34647328127020b3507fc8cddc337038e33fbd7a99ef0d2c7b6a278d7f8116162560
# ---------------------------------------
# seckey: 1006
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003ee
# pubKey (uncompressed): (x: fcecff9ae0490f723123822c66f36996d237490d6769ee68f9f7a7da1c6bac8b5c3d0c4348e8ce8fc3d5159f8333484, y: 86e75481cf86317947ced9b0c52a631a22a213e49b9ea0cd016184d48541e9f2424a5e01a800673b7a2b2601cb77bea)
# pubKey (compressed): 8fcecff9ae0490f723123822c66f36996d237490d6769ee68f9f7a7da1c6bac8b5c3d0c4348e8ce8fc3d5159f8333484
# ---------------------------------------
# seckey: 1007
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003ef
# pubKey (uncompressed): (x: f4ffe81a50cf117069c9a66ad9f2776eeeae94fe02ba2a0f9596cb798f9e5bdf4719fceaa61746ffe2408f25b56d96e, y: 326c5937def2d0725be78d653b1e107c8faf40fea0759caf640ae0be5c569ef73ecdcc1d8552725f8de69e95f4cf53c)
# pubKey (compressed): 8f4ffe81a50cf117069c9a66ad9f2776eeeae94fe02ba2a0f9596cb798f9e5bdf4719fceaa61746ffe2408f25b56d96e
# ---------------------------------------
# seckey: 1008
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003f0
# pubKey (uncompressed): (x: 785405f275ee2fd934e83835a79ba651f80b0f432df1b806350dc949c169c60e60767e41faed8eaac5ed0e9e210787c, y: c82aaba7cb0db559d0eb9cb1bebb8d9de2ac1bbceda92518b16bdca4be5bda5b219b345ec2b3719fac5891eb3ee531a)
# pubKey (compressed): 8785405f275ee2fd934e83835a79ba651f80b0f432df1b806350dc949c169c60e60767e41faed8eaac5ed0e9e210787c
# ---------------------------------------
# seckey: 1009
# seckey hex: 00000000000000000000000000000000000000000000000000000000000003f1
# pubKey (uncompressed): (x: ade2091378293a63d55328cef23736f4dbdc49bd3c0787b8c18cd6a8ddc2d42a279242e87b22d1909f3f1d55e5da66, y: 14f22ce1b5483fa15b71f81d998cbb695a369948214bf7d7c9841c26903cee7b5485bc1331061f1c9c17cce8778b15e)
# pubKey (compressed): 80ade2091378293a63d55328cef23736f4dbdc49bd3c0787b8c18cd6a8ddc2d42a279242e87b22d1909f3f1d55e5da66