curve-secp256k1 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.md +7 -0
- data/README.md +44 -0
- data/lib/secp256k1.rb +57 -0
- data/lib/secp256k1/key.rb +60 -0
- data/lib/secp256k1/pkey.rb +75 -0
- data/lib/secp256k1/version.rb +3 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 906d28b20c856a8be44cf48232b5191f4a28fa14bf2d9d05d5d09d78f68367ec
|
4
|
+
data.tar.gz: 1643f68d77f29ceae8cc4826a9aae8bc1530c5e8a08d226897bc8dbb232e2808
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 69edcc4edf833ed5f4bcbcf30b9fa49ce7715039cb120f832107ab86f746689af813de89c4734f05491d8bde5bc9a535361d508619df92ed43e9e6574268f644
|
7
|
+
data.tar.gz: 582c3bf177b3d9150cfb79bc3dc73d7c5180a42c71333810c7bb985112d72daf31bf69e466707d5e9d7c96bd65817ef015faef261a04a534baace3e91b001c2d
|
data/LICENSE.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2021 Crypto Jane
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# secp256k1 wrapper for Ruby
|
2
|
+
|
3
|
+
This gem wraps libsecp256k1 from the Bitcoin source in a couple of Ruby classes.
|
4
|
+
|
5
|
+
Use in a pipeline to generate crypto-currency addresses and for BIP32 operations (public key addition).
|
6
|
+
|
7
|
+
**To use this gem, libsecp256k1 must be installed on your system**
|
8
|
+
|
9
|
+
## Usage
|
10
|
+
|
11
|
+
Generate a new private key:
|
12
|
+
|
13
|
+
key = Secp256k1::Key.new
|
14
|
+
|
15
|
+
Get a compressed public key (used by most cryptocurrencies):
|
16
|
+
|
17
|
+
key.public_key
|
18
|
+
|
19
|
+
Load an existing private key:
|
20
|
+
|
21
|
+
key = Secp256k1::Key.new("256-bit binary private key value")
|
22
|
+
|
23
|
+
## Installing secp256k1
|
24
|
+
|
25
|
+
### On a Debian/Ubuntu:
|
26
|
+
|
27
|
+
sudo apt install libsecp256k1-dev
|
28
|
+
|
29
|
+
### On macOs:
|
30
|
+
|
31
|
+
It still doesn't come packaged with brew by default but there are a few taps where you can find it.
|
32
|
+
|
33
|
+
### Install from source:
|
34
|
+
|
35
|
+
git clone https://github.com/bitcoin-core/secp256k1
|
36
|
+
cd secp256k1
|
37
|
+
./autogen.sh
|
38
|
+
./configure --enable-module-recovery --enable-experimental --enable-module-ecdh --enable-module-schnorr
|
39
|
+
make && sudo make install
|
40
|
+
|
41
|
+
## TODO
|
42
|
+
|
43
|
+
- Signing and verification
|
44
|
+
- Embed libsecp256k1 in `ext`? (as a git submodule)
|
data/lib/secp256k1.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
require_relative './secp256k1/key'
|
4
|
+
require_relative './secp256k1/pkey'
|
5
|
+
|
6
|
+
module Secp256k1
|
7
|
+
extend FFI::Library
|
8
|
+
ffi_lib 'secp256k1'
|
9
|
+
|
10
|
+
ORDER = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
|
11
|
+
|
12
|
+
# Args:
|
13
|
+
# int: flags for context initialization. 0x301 prepares the context for signing and verifying
|
14
|
+
attach_function :secp256k1_context_create, [:int], :pointer
|
15
|
+
|
16
|
+
# Args:
|
17
|
+
# pointer (1): context
|
18
|
+
# pointer (2): 32 byte buffer with the private key
|
19
|
+
# Returns
|
20
|
+
# 1 if the private key is valid
|
21
|
+
attach_function :secp256k1_ec_seckey_verify, %i[pointer pointer], :int
|
22
|
+
|
23
|
+
# Args:
|
24
|
+
# pointer (1): context
|
25
|
+
# pointer (2): out pointer
|
26
|
+
# pointer (3): private key
|
27
|
+
# Returns
|
28
|
+
# 1 if the private key is valid
|
29
|
+
attach_function :secp256k1_ec_pubkey_create, %i[pointer pointer pointer], :int
|
30
|
+
|
31
|
+
# Args:
|
32
|
+
# pointer (1): context
|
33
|
+
# pointer (2): out buffer
|
34
|
+
# pointer (3): out buffer size
|
35
|
+
# pointer (4): uncompressed public key
|
36
|
+
# int: flags. 0x102 means "compress"
|
37
|
+
attach_function :secp256k1_ec_pubkey_serialize, %i[pointer pointer pointer pointer int], :int
|
38
|
+
|
39
|
+
# Args:
|
40
|
+
# pointer (1): context
|
41
|
+
# pointer (2): resulting public key
|
42
|
+
# pointer (3): pointer to array of pointers to public keys
|
43
|
+
# long (4): number of public keys
|
44
|
+
# Returns
|
45
|
+
# 1 on success
|
46
|
+
attach_function :secp256k1_ec_pubkey_combine, %i[pointer pointer pointer long], :int
|
47
|
+
|
48
|
+
def self.context
|
49
|
+
@context ||= secp256k1_context_create(0x301)
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.key_to_pointer(key)
|
53
|
+
ptr = FFI::MemoryPointer.new(:uint8, 32)
|
54
|
+
ptr.write_bytes(key)
|
55
|
+
ptr
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Secp256k1
|
4
|
+
class Key
|
5
|
+
def initialize(key = nil)
|
6
|
+
if key
|
7
|
+
if valid_key?(key)
|
8
|
+
@key = key.force_encoding('BINARY')
|
9
|
+
else
|
10
|
+
raise ArgumentError, "Invalid secp256k1 private key"
|
11
|
+
end
|
12
|
+
else
|
13
|
+
@key = random_key
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def private_key
|
18
|
+
@key
|
19
|
+
end
|
20
|
+
|
21
|
+
def public_key
|
22
|
+
PKey.new(to_pubkey)
|
23
|
+
end
|
24
|
+
|
25
|
+
def ==(other)
|
26
|
+
@key == other.private_key
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def random_key
|
32
|
+
loop do
|
33
|
+
key = SecureRandom.random_bytes(32)
|
34
|
+
return key if valid_key?(key)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def valid_key?(key)
|
39
|
+
kptr = Secp256k1.key_to_pointer(key)
|
40
|
+
valid = Secp256k1.secp256k1_ec_seckey_verify(Secp256k1.context, kptr) == 1
|
41
|
+
|
42
|
+
kptr.free
|
43
|
+
|
44
|
+
valid
|
45
|
+
end
|
46
|
+
|
47
|
+
# Caller must call #free
|
48
|
+
def to_pubkey
|
49
|
+
out = FFI::MemoryPointer.new(64)
|
50
|
+
priv = Secp256k1.key_to_pointer(@key)
|
51
|
+
Secp256k1.secp256k1_ec_pubkey_create(Secp256k1.context, out, priv)
|
52
|
+
|
53
|
+
val = out.read_bytes(64)
|
54
|
+
out.free
|
55
|
+
priv.free
|
56
|
+
|
57
|
+
val
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Secp256k1
|
4
|
+
class PKey
|
5
|
+
def initialize(uncompressed)
|
6
|
+
@raw = uncompressed.force_encoding('BINARY')
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
@to_s ||= to_compressed
|
11
|
+
end
|
12
|
+
|
13
|
+
def uncompressed
|
14
|
+
@raw
|
15
|
+
end
|
16
|
+
|
17
|
+
def +(other)
|
18
|
+
cptrs = [to_pointer, other.send(:to_pointer)]
|
19
|
+
cptrs_ptr = FFI::MemoryPointer.new(:pointer, 2)
|
20
|
+
|
21
|
+
cptrs_ptr.put_pointer(0, cptrs[0])
|
22
|
+
cptrs_ptr.put_pointer(8, cptrs[1])
|
23
|
+
|
24
|
+
out = FFI::MemoryPointer.new(64)
|
25
|
+
|
26
|
+
valid = Secp256k1.secp256k1_ec_pubkey_combine(
|
27
|
+
Secp256k1.context,
|
28
|
+
out,
|
29
|
+
cptrs_ptr,
|
30
|
+
2
|
31
|
+
)
|
32
|
+
|
33
|
+
result = PKey.new(out.read_bytes(64)) if valid == 1
|
34
|
+
|
35
|
+
out.free
|
36
|
+
cptrs[0].free
|
37
|
+
cptrs[1].free
|
38
|
+
cptrs_ptr.free
|
39
|
+
|
40
|
+
result
|
41
|
+
end
|
42
|
+
|
43
|
+
def ==(other)
|
44
|
+
@raw == other.uncompressed
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
# Caller must call #free
|
50
|
+
def to_pointer
|
51
|
+
out = FFI::MemoryPointer.new(64)
|
52
|
+
out.write_bytes(@raw)
|
53
|
+
out
|
54
|
+
end
|
55
|
+
|
56
|
+
def to_compressed
|
57
|
+
cptr = to_pointer
|
58
|
+
|
59
|
+
out = FFI::MemoryPointer.new(:char, 33)
|
60
|
+
size_pointer = FFI::MemoryPointer.new(:long)
|
61
|
+
size_pointer.write(:long, 33)
|
62
|
+
Secp256k1.secp256k1_ec_pubkey_serialize(
|
63
|
+
Secp256k1.context, out, size_pointer, cptr, 0x102
|
64
|
+
)
|
65
|
+
|
66
|
+
result = out.read_bytes(33)
|
67
|
+
|
68
|
+
out.free
|
69
|
+
size_pointer.free
|
70
|
+
cptr.free
|
71
|
+
|
72
|
+
result
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: curve-secp256k1
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Crypto Jane
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-01-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description:
|
42
|
+
email:
|
43
|
+
- cryptojanedoe@protonmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- LICENSE.md
|
49
|
+
- README.md
|
50
|
+
- lib/secp256k1.rb
|
51
|
+
- lib/secp256k1/key.rb
|
52
|
+
- lib/secp256k1/pkey.rb
|
53
|
+
- lib/secp256k1/version.rb
|
54
|
+
homepage: https://gitlab.com/cryptojane/secp256k1
|
55
|
+
licenses:
|
56
|
+
- MIT
|
57
|
+
metadata:
|
58
|
+
allowed_push_host: https://rubygems.org
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.2.3
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Ruby wrapper for secp256k1 elliptic curve library. Commonly used to manage
|
78
|
+
Cryptocurrency addresses
|
79
|
+
test_files: []
|