Skip to content

Commit

Permalink
add bls (#29)
Browse files Browse the repository at this point in the history
* add blst work

* fix missing submodule

* fix

* bls struct

* fix

* fix

* fix

* let pub key

* swift testing

* try 0.10.0
  • Loading branch information
qiweiii authored Jul 3, 2024
1 parent 2654e72 commit bee2b48
Show file tree
Hide file tree
Showing 16 changed files with 339 additions and 30 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ jobs:
steps:
- name: Checkout Code
uses: actions/checkout@v4
with:
submodules: recursive
- uses: actions/cache@v4
with:
path: '**/.build'
Expand Down
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[submodule "blst"]
path = Utils/Sources/blst
url = https://github.com/supranational/blst.git
8 changes: 6 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,16 @@
.PHONY: githooks
githooks: .git/hooks/pre-commit

.PHONY: deps
deps: githooks
./scripts/deps.sh

.PHONY: test
test: githooks
test: githooks deps
./scripts/run.sh test

.PHONY: build
build: githooks
build: githooks deps
./scripts/run.sh build

.PHONY: clean
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ JAM built with Swift
- SwiftLint: `brew install swiftlint`
- SwiftFormat: `brew install swiftformat`
- Precommit hooks: `make githooks`
- Pull submodules: `git submodule update --init --recursive`
- Setup deps: `make deps`

## Packages

Expand Down
20 changes: 19 additions & 1 deletion Utils/Package.resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "7f9d01d4c8c5ccb64f9b9f466f7f8e61b0fd8cd435aa5c4e587e5a6e459f5d77",
"originHash" : "e52d41427ac86e2fe9044bdaf7129a42c2be6dd8550156992e8b20f787f1cc2e",
"pins" : [
{
"identity" : "blake2.swift",
Expand Down Expand Up @@ -28,6 +28,24 @@
"version" : "3.4.0"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-syntax.git",
"state" : {
"revision" : "4c6cc0a3b9e8f14b3ae2307c5ccae4de6167ac2c",
"version" : "600.0.0-prerelease-2024-06-12"
}
},
{
"identity" : "swift-testing",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-testing.git",
"state" : {
"branch" : "0.10.0",
"revision" : "69d59cfc76e5daf498ca61f5af409f594768eef9"
}
},
{
"identity" : "tuples.swift",
"kind" : "remoteSourceControl",
Expand Down
22 changes: 21 additions & 1 deletion Utils/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ let package = Package(
.package(url: "https://github.com/tesseract-one/ScaleCodec.swift.git", from: "0.3.0"),
.package(url: "https://github.com/tesseract-one/Blake2.swift.git", from: "0.2.0"),
.package(url: "https://github.com/apple/swift-crypto.git", "1.0.0" ..< "4.0.0"),
.package(url: "https://github.com/apple/swift-testing.git", branch: "0.10.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand All @@ -29,11 +30,30 @@ let package = Package(
.product(name: "ScaleCodec", package: "ScaleCodec.swift"),
.product(name: "Blake2", package: "Blake2.swift"),
.product(name: "Crypto", package: "swift-crypto"),
"blst",
]
),
.target(
name: "blst",
dependencies: [],
path: "./Sources/blst",
exclude: [
".github",
"./build",
"./src",
],
sources: [],
publicHeadersPath: "./include",
cSettings: [
.headerSearchPath("./include"),
],
linkerSettings: [
.unsafeFlags(["-L../Utils/Sources/blst/lib", "-lblst"]),
]
),
.testTarget(
name: "UtilsTests",
dependencies: ["Utils"]
dependencies: ["Utils", .product(name: "Testing", package: "swift-testing")]
),
],
swiftLanguageVersions: [.version("6")]
Expand Down
161 changes: 161 additions & 0 deletions Utils/Sources/Utils/BLS.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
import blst
import Foundation

enum BLSError: Error {
case ikmTooShort
case blstError(BLST_ERROR)
}

/// A wrapper to blst C library.
///
/// `blst_p1` for public keys, and `blst_p2` for signatures
public struct BLS {
public let secretKey: Data32

public let publicKey: Data48

/// Initiate a BLS secret key with IKM.
/// IKM MUST be infeasible to guess, e.g., generated by a trusted source of randomness.
/// IKM MUST be at least 32 bytes long, but it MAY be longer.
public init(ikm: Data) throws {
guard ikm.count >= 32 else {
throw BLSError.ikmTooShort
}

var sk = blst_scalar()
let ikmBytes = [UInt8](ikm)
let ikmLen = ikmBytes.count

blst_keygen(&sk, ikmBytes, ikmLen, nil, 0)

var out = [UInt8](repeating: 0, count: 32)
blst_bendian_from_scalar(&out, &sk)

secretKey = Data32(Data(out))!
publicKey = BLS.getPublicKey(secretKey)
}

public init(privateKey: Data32) throws {
var sk = blst_scalar()
blst_scalar_from_bendian(&sk, [UInt8](privateKey.data))

guard blst_sk_check(&sk) else {
throw BLSError.blstError(BLST_BAD_SCALAR)
}

secretKey = privateKey
publicKey = BLS.getPublicKey(secretKey)
}

private static func getPublicKey(_ secretKey: Data32) -> Data48 {
var sk = blst_scalar()
blst_scalar_from_bendian(&sk, [UInt8](secretKey.data))

var pk = blst_p1()
blst_sk_to_pk_in_g1(&pk, &sk)

var pkBytes = [UInt8](repeating: 0, count: 48)
blst_p1_compress(&pkBytes, &pk)

return Data48(Data(pkBytes))!
}

public func sign(message: Data) -> Data96 {
var sk = blst_scalar()
blst_scalar_from_bendian(&sk, [UInt8](secretKey.data))

var msgHash = blst_p2()
blst_hash_to_g2(&msgHash, [UInt8](message), message.count, nil, 0, nil, 0)

var sig = blst_p2()
blst_sign_pk_in_g1(&sig, &msgHash, &sk)

var sigBytes = [UInt8](repeating: 0, count: 96)
blst_p2_compress(&sigBytes, &sig)

return Data96(Data(sigBytes))!
}

public static func verify(signature: Data96, message: Data, publicKey: Data48) -> Bool {
var pk = blst_p1_affine()
var sig = blst_p2_affine()

let pkResult = blst_p1_uncompress(&pk, [UInt8](publicKey.data))
let sigResult = blst_p2_uncompress(&sig, [UInt8](signature.data))

guard pkResult == BLST_SUCCESS, sigResult == BLST_SUCCESS else {
return false
}

let verifyResult = blst_core_verify_pk_in_g1(
&pk, &sig, true, [UInt8](message), message.count, nil, 0, nil, 0
)

return verifyResult == BLST_SUCCESS
}

public static func aggregateVerify(
signature: Data96, messages: [Data], publicKeys: [Data48]
)
-> Bool
{
let size = blst_pairing_sizeof()
let ctx = OpaquePointer(malloc(size))

blst_pairing_init(ctx, true, nil, 0)

var sig = blst_p2_affine()
let sigResult = blst_p2_uncompress(&sig, [UInt8](signature.data))
guard sigResult == BLST_SUCCESS else {
return false
}

for i in 0 ..< publicKeys.count {
var pk = blst_p1_affine()
let pkResult = blst_p1_uncompress(&pk, [UInt8](publicKeys[i].data))
guard pkResult == BLST_SUCCESS else {
return false
}

let aggregateResult: BLST_ERROR =
if i == 1 {
blst_pairing_aggregate_pk_in_g1(
ctx, &pk, &sig, [UInt8](messages[i]), messages[i].count, nil, 0
)
} else {
blst_pairing_aggregate_pk_in_g1(
ctx, &pk, nil, [UInt8](messages[i]), messages[i].count, nil, 0
)
}
guard aggregateResult == BLST_SUCCESS else {
return false
}
}

blst_pairing_commit(ctx)
let result = blst_pairing_finalverify(ctx, nil)

free(UnsafeMutableRawPointer(ctx))

return result
}

public static func aggregateSignatures(signatures: [Data96]) throws -> Data96 {
var aggregate = blst_p2()

for signature in signatures {
var sig = blst_p2_affine()
let sigResult = blst_p2_uncompress(&sig, [UInt8](signature.data))
guard sigResult == BLST_SUCCESS else {
throw BLSError.blstError(sigResult)
}
var aggCopy = aggregate
blst_p2_add_or_double_affine(&aggregate, &aggCopy, &sig)
}

var sigCompressed = [UInt8](repeating: 0, count: 96)
blst_p2_compress(&sigCompressed, &aggregate)

return Data96(Data(sigCompressed))!
}
}
6 changes: 6 additions & 0 deletions Utils/Sources/Utils/ConstValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ public enum ConstInt32: ConstInt {
}
}

public enum ConstInt48: ConstInt {
public static var value: Int {
48
}
}

public enum ConstInt64: ConstInt {
public static var value: Int {
64
Expand Down
5 changes: 3 additions & 2 deletions Utils/Sources/Utils/Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ extension Data {
var index = hexString.startIndex

while index < hexString.endIndex {
guard let nextIndex = hexString.index(index, offsetBy: 2, limitedBy: hexString.endIndex),
let byte = UInt8(hexString[index ..< nextIndex], radix: 16)
guard
let nextIndex = hexString.index(index, offsetBy: 2, limitedBy: hexString.endIndex),
let byte = UInt8(hexString[index ..< nextIndex], radix: 16)
else {
return nil
}
Expand Down
1 change: 1 addition & 0 deletions Utils/Sources/Utils/FixedSizeData.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ extension FixedSizeData: ScaleCodec.Codable {
}

public typealias Data32 = FixedSizeData<ConstInt32>
public typealias Data48 = FixedSizeData<ConstInt48>
public typealias Data64 = FixedSizeData<ConstInt64>
public typealias Data96 = FixedSizeData<ConstUInt96>
public typealias Data128 = FixedSizeData<ConstUInt128>
Expand Down
1 change: 1 addition & 0 deletions Utils/Sources/blst
Submodule blst added at e99f7d
55 changes: 55 additions & 0 deletions Utils/Tests/UtilsTests/BLSTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import Foundation
import Testing

@testable import Utils

@Suite struct BLSTests {
@Test func BLSSignatureWorks() throws {
let bls = try BLS(ikm: Data("this is random high entropy ikm for key1".utf8))
let publicKey1 = bls.publicKey
let message1 = Data("test1".utf8)
let signature1 = bls.sign(message: message1)

#expect(
BLS.verify(signature: signature1, message: message1, publicKey: publicKey1)
)

let invalidMessage = Data("testUnknown".utf8)
#expect(
!BLS.verify(signature: signature1, message: invalidMessage, publicKey: publicKey1)
)

var invalidSignature = signature1.data
invalidSignature.replaceSubrange(0 ... 1, with: [2, 3])
#expect(
!BLS.verify(
signature: Data96(invalidSignature)!, message: message1, publicKey: publicKey1
)
)

let bls2 = try BLS(ikm: Data("this is random high entropy ikm for key2".utf8))
let publicKey2 = bls2.publicKey
let message2 = Data("test2".utf8)
let signature2 = bls2.sign(message: message2)

#expect(
BLS.verify(signature: signature2, message: message2, publicKey: publicKey2)
)

let aggSig = try BLS.aggregateSignatures(signatures: [signature1, signature2])

#expect(
BLS.aggregateVerify(
signature: aggSig, messages: [message1, message2],
publicKeys: [publicKey1, publicKey2]
)
)

#expect(
!BLS.aggregateVerify(
signature: aggSig, messages: [message1, message2],
publicKeys: [publicKey2, publicKey1]
)
)
}
}
13 changes: 8 additions & 5 deletions Utils/Tests/UtilsTests/Blake2Tests.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import XCTest
import Foundation
import Testing

@testable import Utils

final class Blake2Tests: XCTestCase {
func testBlake2b256Test() throws {
@Suite struct Blake2Tests {
@Test func blake2b256Works() throws {
let testData = Data("test".utf8)
let expected = Data(fromHexString: "928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202")
let expected = Data(
fromHexString: "928b20366943e2afd11ebc0eae2e53a93bf177a4fcf35bcc64d503704e65e202"
)
let actual = try blake2b256(testData)
XCTAssertEqual(expected, actual.data)
#expect(expected == actual.data)
}
}
Loading

0 comments on commit bee2b48

Please sign in to comment.