nostr 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.adr-dir +1 -0
- data/.rubocop.yml +1 -1
- data/.tool-versions +2 -2
- data/CHANGELOG.md +35 -1
- data/README.md +2 -1
- data/adr/0001-record-architecture-decisions.md +19 -0
- data/adr/0002-introduction-of-signature-class.md +27 -0
- data/docs/.vitepress/config.mjs +2 -0
- data/docs/common-use-cases/signing-and-verifying-events.md +50 -0
- data/docs/common-use-cases/signing-and-verifying-messages.md +43 -0
- data/docs/getting-started/overview.md +2 -1
- data/docs/index.md +0 -2
- data/lib/nostr/crypto.rb +82 -6
- data/lib/nostr/errors/invalid_key_format_error.rb +1 -1
- data/lib/nostr/errors/invalid_key_length_error.rb +1 -1
- data/lib/nostr/errors/invalid_key_type_error.rb +1 -1
- data/lib/nostr/errors/invalid_signature_format_error.rb +18 -0
- data/lib/nostr/errors/invalid_signature_length_error.rb +18 -0
- data/lib/nostr/errors/invalid_signature_type_error.rb +16 -0
- data/lib/nostr/errors/signature_validation_error.rb +6 -0
- data/lib/nostr/errors.rb +4 -0
- data/lib/nostr/event.rb +37 -9
- data/lib/nostr/event_kind.rb +1 -0
- data/lib/nostr/events/encrypted_direct_message.rb +4 -4
- data/lib/nostr/filter.rb +10 -7
- data/lib/nostr/key.rb +2 -2
- data/lib/nostr/key_pair.rb +26 -2
- data/lib/nostr/keygen.rb +1 -1
- data/lib/nostr/signature.rb +67 -0
- data/lib/nostr/version.rb +1 -1
- data/lib/nostr.rb +1 -0
- data/nostr.gemspec +8 -8
- data/sig/nostr/crypto.rbs +3 -0
- data/sig/nostr/errors/invalid_signature_format_error.rbs +5 -0
- data/sig/nostr/errors/invalid_signature_length_error.rbs +5 -0
- data/sig/nostr/errors/invalid_signature_type_error.rbs +5 -0
- data/sig/nostr/errors/signature_validation_error.rbs +4 -0
- data/sig/nostr/event.rbs +7 -6
- data/sig/nostr/key_pair.rbs +1 -0
- data/sig/nostr/signature.rbs +14 -0
- data/sig/vendor/schnorr/signature.rbs +16 -0
- data/sig/vendor/schnorr.rbs +3 -1
- metadata +34 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dcfeb88b78cad383e565b6e908be58f58e6f1002083044957ee0f09cf63228f4
|
4
|
+
data.tar.gz: ca2229181586e8bf5f329e4d5ac60ad346076fc8e39281dd43690d57ac98fcb7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4511decc634548b75429db9166433060795295aea61cda64cd7b2b2647415db3808f4def27f8a4806d77b6fbaef88b11d08788254febd66bcfb7be318059cced
|
7
|
+
data.tar.gz: aef568e9fad07e0b387e3ae1437a50d08c81b59485c8aadd42cd9d7a035319b3da2bd5247e7d58fc890961eec5e0284d9299e6631c674cce3df3a5c210202bf2
|
data/.adr-dir
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
adr
|
data/.rubocop.yml
CHANGED
data/.tool-versions
CHANGED
@@ -1,2 +1,2 @@
|
|
1
|
-
ruby 3.
|
2
|
-
bun 1.0.
|
1
|
+
ruby 3.3.0
|
2
|
+
bun 1.0.30
|
data/CHANGELOG.md
CHANGED
@@ -4,13 +4,46 @@ All notable changes to this project will be documented in this file.
|
|
4
4
|
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.1.1/)
|
5
5
|
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
6
6
|
|
7
|
+
## [0.6.0] 2024-03-15
|
8
|
+
|
9
|
+
### Added
|
10
|
+
|
11
|
+
- Added Architecture Decision Records (ADRs) to document architectural decisions
|
12
|
+
- Added the `Signature` class to fix the primitive obsession with signatures and to make it easier to work with them
|
13
|
+
- Added `valid_sig?` and `check_sig!` to the `Crypto` class to verify whether an event's signature is valid
|
14
|
+
- Added `sign_message` to the `Crypto` class to sign a message
|
15
|
+
- Added `verify_signature?` to the `Event` class to verify whether an event's signature is valid
|
16
|
+
- Added `#to_ary` to the `KeyPair` class to enable keypair destructuring
|
17
|
+
- Added RBS types for `schnorr`
|
18
|
+
|
19
|
+
### Changed
|
20
|
+
|
21
|
+
- Updated the required Ruby version to `3.3.0` (was `3.2.0`)
|
22
|
+
- Updated the gem `dotenv` to version `3.1` (was `2.8`)
|
23
|
+
- Updated the gem `bip-schnorr` to version `0.7` (was `0.6`)
|
24
|
+
- Updated the gem `overcommit` to version `0.63` (was `0.59`)
|
25
|
+
- Updated the gem `rbs` to version `3.4` (was `3.3`)
|
26
|
+
- Updated the gem `rspec` to version `3.13` (was `3.12`)
|
27
|
+
- Updated the gem `rspec-rubocop` to version `2.27` (was `2.25`)
|
28
|
+
- Updated the gem `rubocop` to version `1.62` (was `1.57`)
|
29
|
+
|
30
|
+
## Fixed
|
31
|
+
|
32
|
+
- Fixed a typo in the README (`generate_keypair` -> `generate_key_pair`)
|
33
|
+
- Fixed a typo in the YARD documentation of `Nostr::Key#initialize` (`ValidationError` -> `KeyValidationError`)
|
34
|
+
|
35
|
+
### Fixed
|
36
|
+
|
37
|
+
- Fixed YARD example rendering issues in `InvalidKeyFormatError#initialize`, `InvalidKeyLengthError#initialize`,
|
38
|
+
`InvalidKeyTypeError#initialize`, `Event#initialize`, `EncryptedDirectMessage#initialize` and `Filter#to_h`
|
39
|
+
|
7
40
|
## [0.5.0] 2023-11-20
|
8
41
|
|
9
42
|
### Added
|
10
43
|
|
11
44
|
- Added relay message type enums `Nostr::RelayMessageType`
|
12
45
|
- Compliance with [NIP-19](https://github.com/nostr-protocol/nips/blob/master/19.md) - bech32-formatted strings
|
13
|
-
- `Nostr::PrivateKey` and `Nostr::PublicKey` to represent private and public keys, respectively
|
46
|
+
- Added `Nostr::PrivateKey` and `Nostr::PublicKey` to represent private and public keys, respectively
|
14
47
|
- Added a validation of private and public keys
|
15
48
|
- Added an ability to convert keys to and from Bech32 format
|
16
49
|
- Added RBS types for `faye-websocket` and `bech32`
|
@@ -82,6 +115,7 @@ principles of immutability and was a major source of internal complexity as I ne
|
|
82
115
|
|
83
116
|
- Initial release
|
84
117
|
|
118
|
+
[0.6.0]: https://github.com/wilsonsilva/nostr/compare/v0.5.0...v0.6.0
|
85
119
|
[0.5.0]: https://github.com/wilsonsilva/nostr/compare/v0.4.0...v0.5.0
|
86
120
|
[0.4.0]: https://github.com/wilsonsilva/nostr/compare/v0.3.0...v0.4.0
|
87
121
|
[0.3.0]: https://github.com/wilsonsilva/nostr/compare/v0.2.0...v0.3.0
|
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# Nostr
|
2
2
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/nostr.svg)](https://badge.fury.io/rb/nostr)
|
4
|
+
![Build](https://github.com/wilsonsilva/nostr/actions/workflows/main.yml/badge.svg)
|
4
5
|
[![Maintainability](https://api.codeclimate.com/v1/badges/c7633eb2c89eb95ee7f2/maintainability)](https://codeclimate.com/github/wilsonsilva/nostr/maintainability)
|
5
6
|
[![Test Coverage](https://api.codeclimate.com/v1/badges/c7633eb2c89eb95ee7f2/test_coverage)](https://codeclimate.com/github/wilsonsilva/nostr/test_coverage)
|
6
7
|
|
@@ -63,7 +64,7 @@ keypair = keygen.get_key_pair_from_private_key(
|
|
63
64
|
|
64
65
|
# c) Or create a new keypair
|
65
66
|
keygen = Nostr::Keygen.new
|
66
|
-
keypair = keygen.
|
67
|
+
keypair = keygen.generate_key_pair
|
67
68
|
|
68
69
|
# Create a user with the keypair
|
69
70
|
user = Nostr::User.new(keypair: keypair)
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# 1. Record architecture decisions
|
2
|
+
|
3
|
+
Date: 2024-03-13
|
4
|
+
|
5
|
+
## Status
|
6
|
+
|
7
|
+
Accepted
|
8
|
+
|
9
|
+
## Context
|
10
|
+
|
11
|
+
We need to record the architectural decisions made on this project.
|
12
|
+
|
13
|
+
## Decision
|
14
|
+
|
15
|
+
We will use Architecture Decision Records, as [described by Michael Nygard](http://thinkrelevance.com/blog/2011/11/15/documenting-architecture-decisions).
|
16
|
+
|
17
|
+
## Consequences
|
18
|
+
|
19
|
+
See Michael Nygard's article, linked above. For a lightweight ADR toolset, see Nat Pryce's [adr-tools](https://github.com/npryce/adr-tools).
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# 2. introduction-of-signature-class
|
2
|
+
|
3
|
+
Date: 2024-03-14
|
4
|
+
|
5
|
+
## Status
|
6
|
+
|
7
|
+
Accepted
|
8
|
+
|
9
|
+
## Context
|
10
|
+
|
11
|
+
I noticed significant overuse of primitive strings for signatures, which led to widespread and repetitive validation logic, increasing the potential for errors and making the system harder to manage and maintain.
|
12
|
+
|
13
|
+
## Decision
|
14
|
+
|
15
|
+
I introduced the Nostr::Signature class, choosing to subclass String to leverage string-like behavior while embedding specific validation rules for signatures. This move was aimed at streamlining validation, ensuring consistency, and maintaining the usability of strings.
|
16
|
+
|
17
|
+
## Consequences
|
18
|
+
|
19
|
+
### Positive
|
20
|
+
|
21
|
+
- This design choice has made the codebase cleaner and more robust, reducing the chances of errors related to signature handling. It ensures that all signature instances are valid at creation, leveraging the familiarity and flexibility of string operations without sacrificing the integrity of the data. Moreover, it sets a strong foundation for extending signature-related functionality in the future.
|
22
|
+
|
23
|
+
### Negative
|
24
|
+
|
25
|
+
- __Performance Concerns:__ Subclassing String might introduce slight performance overheads due to the additional validation logic executed upon instantiation of a Signature object.
|
26
|
+
- __Integration Challenges:__ Integrating this class into existing systems where strings were used indiscriminately for signatures requires careful refactoring to ensure compatibility. There's also the potential for issues when passing Nostr::Signature objects to libraries or APIs expecting plain strings without the additional constraints.
|
27
|
+
- __Learning Curve:__ For new team members or contributors, understanding the necessity and functionality of the Nostr::Signature class adds to the learning curve, potentially slowing down initial development efforts as they familiarize themselves with the custom implementation.
|
data/docs/.vitepress/config.mjs
CHANGED
@@ -79,6 +79,8 @@ export default defineConfig(withMermaid({
|
|
79
79
|
collapsed: false,
|
80
80
|
items: [
|
81
81
|
{ text: 'Bech32 enc/decoding (NIP-19)', link: '/common-use-cases/bech32-encoding-and-decoding-(NIP-19)' },
|
82
|
+
{ text: 'Signing/verifying messages', link: '/common-use-cases/signing-and-verifying-messages' },
|
83
|
+
{ text: 'Signing/verifying events', link: '/common-use-cases/signing-and-verifying-events' },
|
82
84
|
]
|
83
85
|
},
|
84
86
|
{
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# Signing and verifying events
|
2
|
+
|
3
|
+
Signing an event in Nostr proves it was sent by the owner of a specific private key.
|
4
|
+
|
5
|
+
## Signing an event
|
6
|
+
|
7
|
+
To sign an event, use the private key associated with the event's creator. Here's how to sign a message using a
|
8
|
+
predefined keypair:
|
9
|
+
|
10
|
+
```ruby{14}
|
11
|
+
require 'nostr'
|
12
|
+
|
13
|
+
private_key = Nostr::PrivateKey.new('67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'),
|
14
|
+
public_key = Nostr::PublicKey.new('7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'),
|
15
|
+
|
16
|
+
event = Nostr::Event.new(
|
17
|
+
pubkey: public_key.to_s,
|
18
|
+
kind: Nostr::EventKind::TEXT_NOTE,
|
19
|
+
content: 'We did it with security, now we’re going to do it with the economy.',
|
20
|
+
created_at: Time.now.to_i,
|
21
|
+
)
|
22
|
+
|
23
|
+
# Sign the event with the private key
|
24
|
+
event.sign(private_key)
|
25
|
+
|
26
|
+
puts "Event ID: #{event.id}"
|
27
|
+
puts "Event Signature: #{event.sig}"
|
28
|
+
```
|
29
|
+
|
30
|
+
## Verifying an event's signature
|
31
|
+
|
32
|
+
To verify an event, you must ensure the event's signature is valid. This indicates the event was created by the owner
|
33
|
+
of the corresponding public key.
|
34
|
+
|
35
|
+
When the event was signed with the private key corresponding to the public key, the `verify_signature` method will
|
36
|
+
return `true`.
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
event.verify_signature # => true
|
40
|
+
```
|
41
|
+
|
42
|
+
And when the event was not signed with the private key corresponding to the public key, the `verify_signature` method
|
43
|
+
will return `false`.
|
44
|
+
|
45
|
+
An event without an `id`, `pubkey`, `sig` is considered invalid and will return `false` when calling `verify_signature`.
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
other_public_key = Nostr::PublicKey.new('10be96d345ed58d923a734560680f1adfd2b1006c28ac93b8e1b032a9a32c6e9')
|
49
|
+
event.verify_signature # => false
|
50
|
+
```
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# Signing and verifying messages
|
2
|
+
|
3
|
+
Signing a message in Nostr proves it was sent by the owner of a specific private key.
|
4
|
+
|
5
|
+
## Signing a message
|
6
|
+
|
7
|
+
To sign a message, you'll need a private key. Here's how to sign a message using a predefined keypair:
|
8
|
+
|
9
|
+
```ruby{9}
|
10
|
+
require 'nostr'
|
11
|
+
|
12
|
+
private_key = Nostr::PrivateKey.new('67dea2ed018072d675f5415ecfaed7d2597555e202d85b3d65ea4e58d2d92ffa'),
|
13
|
+
public_key = Nostr::PublicKey.new('7e7e9c42a91bfef19fa929e5fda1b72e0ebc1a4c1141673e2794234d86addf4e'),
|
14
|
+
|
15
|
+
message = 'We did it with security, now we’re going to do it with the economy.' # The message you want to sign
|
16
|
+
|
17
|
+
crypto = Nostr::Crypto.new
|
18
|
+
signature = crypto.sign_message(message, private_key)
|
19
|
+
signature # => "d7a0aac1fadcddf1aa2949bedfcdf25ce0c1604e648e55d31431fdacbff8e8256f7c2166d98292f80bc5f79105a0b6e8a89236a47d97cf5d0e7cc1ebf34dea5c"
|
20
|
+
```
|
21
|
+
|
22
|
+
## Verifying a signature
|
23
|
+
|
24
|
+
To verify a signature, you need the original message, the public key of the signer, and the signature.
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
crypto.valid_sig?(message, public_key, signature) # => true
|
28
|
+
crypto.check_sig!(message, public_key, signature) # => true
|
29
|
+
```
|
30
|
+
|
31
|
+
When the message was not signed with the private key corresponding to the public key, the `valid_sig?` method will return `false`.
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
other_public_key = Nostr::PublicKey.new('10be96d345ed58d923a734560680f1adfd2b1006c28ac93b8e1b032a9a32c6e9')
|
35
|
+
crypto.valid_sig?(message, public_key, signature) # => false
|
36
|
+
```
|
37
|
+
|
38
|
+
And when the message was not signed with the private key corresponding to the public key, the `check_sig!` method will raise an error.
|
39
|
+
|
40
|
+
```ruby
|
41
|
+
other_public_key = Nostr::PublicKey.new('10be96d345ed58d923a734560680f1adfd2b1006c28ac93b8e1b032a9a32c6e9')
|
42
|
+
crypto.check_sig!(message, other_public_key, signature) # => Schnorr::InvalidSignatureError: signature verification failed
|
43
|
+
```
|
@@ -35,6 +35,7 @@ classDiagram
|
|
35
35
|
serialize()
|
36
36
|
to_h()
|
37
37
|
sign(private_key)
|
38
|
+
verify_signature()
|
38
39
|
}
|
39
40
|
class Subscription {
|
40
41
|
id
|
@@ -110,7 +111,7 @@ keypair = keygen.get_key_pair_from_private_key(
|
|
110
111
|
|
111
112
|
# c) Or create a new keypair
|
112
113
|
keygen = Nostr::Keygen.new
|
113
|
-
keypair = keygen.
|
114
|
+
keypair = keygen.generate_key_pair
|
114
115
|
|
115
116
|
# Create a user with the keypair
|
116
117
|
user = Nostr::User.new(keypair: keypair)
|
data/docs/index.md
CHANGED
@@ -39,6 +39,4 @@ features:
|
|
39
39
|
- title: Fully typed
|
40
40
|
details: All code is typed with <a href="https://rubygems.org/gems/rbs" target="_blank">RBS</a> with the help of <a href="https://rubygems.org/gems/typeprof" target="_blank">TypeProf</a>. Type correctness is enforced by <a href="https://rubygems.org/gems/steep" target="_blank">Steep</a>.
|
41
41
|
icon: ✅
|
42
|
-
|
43
42
|
---
|
44
|
-
|
data/lib/nostr/crypto.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Nostr
|
4
|
-
# Performs cryptographic operations
|
4
|
+
# Performs cryptographic operations.
|
5
5
|
class Crypto
|
6
6
|
# Numeric base of the OpenSSL big number used in an event content's encryption.
|
7
7
|
#
|
@@ -90,17 +90,93 @@ module Nostr
|
|
90
90
|
#
|
91
91
|
def sign_event(event, private_key)
|
92
92
|
event_digest = hash_event(event)
|
93
|
-
|
94
|
-
hex_private_key = Array(private_key).pack('H*')
|
95
|
-
hex_message = Array(event_digest).pack('H*')
|
96
|
-
event_signature = Schnorr.sign(hex_message, hex_private_key).encode.unpack1('H*')
|
93
|
+
signature = sign_message(event_digest, private_key)
|
97
94
|
|
98
95
|
event.id = event_digest
|
99
|
-
event.sig =
|
96
|
+
event.sig = signature
|
100
97
|
|
101
98
|
event
|
102
99
|
end
|
103
100
|
|
101
|
+
# Signs a message using the Schnorr signature algorithm
|
102
|
+
#
|
103
|
+
# @api public
|
104
|
+
#
|
105
|
+
# @example Signing a message
|
106
|
+
# crypto = Nostr::Crypto.new
|
107
|
+
# message = 'Viva la libertad carajo'
|
108
|
+
# private_key = Nostr::PrivateKey.new('7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d')
|
109
|
+
# signature = crypto.sign_message(message, private_key)
|
110
|
+
# signature # => 'b2115694a576f5bdcebf8c0951a3c7adcfbdb17b11cb9e6d6b7017691138bc6' \
|
111
|
+
# '38fee642a7bd26f71b313a7057181294198900a9770d1435e43f182acf3d34c26'
|
112
|
+
#
|
113
|
+
# @param [String] message The message to be signed
|
114
|
+
# @param [PrivateKey] private_key The private key used for signing
|
115
|
+
#
|
116
|
+
# @return [Signature] A signature object containing the signature as a 64-byte hexadecimal string.
|
117
|
+
#
|
118
|
+
def sign_message(message, private_key)
|
119
|
+
hex_private_key = Array(private_key).pack('H*')
|
120
|
+
hex_message = Array(message).pack('H*')
|
121
|
+
hex_signature = Schnorr.sign(hex_message, hex_private_key).encode.unpack1('H*')
|
122
|
+
|
123
|
+
Signature.new(hex_signature.to_s)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Verifies the given {Signature} and returns true if it is valid
|
127
|
+
#
|
128
|
+
# @api public
|
129
|
+
#
|
130
|
+
# @example Checking a signature
|
131
|
+
# public_key = Nostr::PublicKey.new('15678d8fbc126fa326fac536acd5a6dcb5ef64b3d939abe31d6830cba6cd26d6')
|
132
|
+
# private_key = Nostr::PrivateKey.new('7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d')
|
133
|
+
# message = 'Viva la libertad carajo'
|
134
|
+
# crypto = Nostr::Crypto.new
|
135
|
+
# signature = crypto.sign_message(message, private_key)
|
136
|
+
# valid = crypto.valid_sig?(message, public_key, signature)
|
137
|
+
# valid # => true
|
138
|
+
#
|
139
|
+
# @see #check_sig!
|
140
|
+
#
|
141
|
+
# @param [String] message A message to be signed with binary format.
|
142
|
+
# @param [PublicKey] public_key The public key with binary format.
|
143
|
+
# @param [Signature] signature The signature with binary format.
|
144
|
+
#
|
145
|
+
# @return [Boolean] whether signature is valid.
|
146
|
+
#
|
147
|
+
def valid_sig?(message, public_key, signature)
|
148
|
+
signature = Schnorr::Signature.decode([signature].pack('H*'))
|
149
|
+
Schnorr.valid_sig?([message].pack('H*'), [public_key].pack('H*'), signature.encode)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Verifies the given {Signature} and raises an +Schnorr::InvalidSignatureError+ if it is invalid
|
153
|
+
#
|
154
|
+
# @api public
|
155
|
+
#
|
156
|
+
# @example Checking a signature
|
157
|
+
# public_key = Nostr::PublicKey.new('15678d8fbc126fa326fac536acd5a6dcb5ef64b3d939abe31d6830cba6cd26d6')
|
158
|
+
# private_key = Nostr::PrivateKey.new('7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d')
|
159
|
+
# message = 'Viva la libertad carajo'
|
160
|
+
# crypto = Nostr::Crypto.new
|
161
|
+
# signature = crypto.sign_message(message, private_key)
|
162
|
+
# valid = crypto.valid_sig?(message, public_key, signature)
|
163
|
+
# valid # => true
|
164
|
+
#
|
165
|
+
# @see #valid_sig?
|
166
|
+
#
|
167
|
+
# @param [String] message A message to be signed with binary format.
|
168
|
+
# @param [PublicKey] public_key The public key with binary format.
|
169
|
+
# @param [Signature] signature The signature with binary format.
|
170
|
+
#
|
171
|
+
# @raise [Schnorr::InvalidSignatureError] if the signature is invalid.
|
172
|
+
#
|
173
|
+
# @return [Boolean] whether signature is valid.
|
174
|
+
#
|
175
|
+
def check_sig!(message, public_key, signature)
|
176
|
+
signature = Schnorr::Signature.decode([signature].pack('H*'))
|
177
|
+
Schnorr.check_sig!([message].pack('H*'), [public_key].pack('H*'), signature.encode)
|
178
|
+
end
|
179
|
+
|
104
180
|
private
|
105
181
|
|
106
182
|
# Finds a shared key between two keys
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nostr
|
4
|
+
# Raised when the signature is in an invalid format
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
#
|
8
|
+
class InvalidSignatureFormatError < SignatureValidationError
|
9
|
+
# Initializes the error
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# InvalidSignatureFormatError.new
|
13
|
+
#
|
14
|
+
def initialize
|
15
|
+
super('Only lowercase hexadecimal characters are allowed in signatures.')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nostr
|
4
|
+
# Raised when the signature's length is not 128 characters
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
#
|
8
|
+
class InvalidSignatureLengthError < SignatureValidationError
|
9
|
+
# Initializes the error
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# InvalidSignatureLengthError.new
|
13
|
+
#
|
14
|
+
def initialize
|
15
|
+
super('Invalid signature length. It should have 128 characters.')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nostr
|
4
|
+
# Raised when the signature is not a string
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
#
|
8
|
+
class InvalidSignatureTypeError < SignatureValidationError
|
9
|
+
# Initializes the error
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# InvalidSignatureTypeError.new
|
13
|
+
#
|
14
|
+
def initialize = super('Invalid signature type')
|
15
|
+
end
|
16
|
+
end
|
data/lib/nostr/errors.rb
CHANGED
@@ -6,3 +6,7 @@ require_relative 'errors/invalid_hrp_error'
|
|
6
6
|
require_relative 'errors/invalid_key_type_error'
|
7
7
|
require_relative 'errors/invalid_key_length_error'
|
8
8
|
require_relative 'errors/invalid_key_format_error'
|
9
|
+
require_relative 'errors/signature_validation_error'
|
10
|
+
require_relative 'errors/invalid_signature_type_error'
|
11
|
+
require_relative 'errors/invalid_signature_length_error'
|
12
|
+
require_relative 'errors/invalid_signature_format_error'
|
data/lib/nostr/event.rb
CHANGED
@@ -100,15 +100,15 @@ module Nostr
|
|
100
100
|
#
|
101
101
|
# @example Instantiating a new event
|
102
102
|
# Nostr::Event.new(
|
103
|
-
#
|
104
|
-
#
|
105
|
-
#
|
106
|
-
#
|
107
|
-
#
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
#
|
103
|
+
# id: 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460',
|
104
|
+
# pubkey: '48df4af6e240ac5f7c5de89bf5941b249880be0e87d03685b178ccb1a315f52e',
|
105
|
+
# created_at: 1230981305,
|
106
|
+
# kind: 1,
|
107
|
+
# tags: [],
|
108
|
+
# content: 'Your feedback is appreciated, now pay $8',
|
109
|
+
# sig: '123ac2923b792ce730b3da34f16155470ab13c8f97f9c53eaeb334f1fb3a5dc9a7f643
|
110
|
+
# 937c6d6e9855477638f5655c5d89c9aa5501ea9b578a66aced4f1cd7b3'
|
111
|
+
# )
|
112
112
|
#
|
113
113
|
# @param id [String|nil] 32-bytes sha256 of the the serialized event data.
|
114
114
|
# @param sig [String|nil] 64-bytes signature of the sha256 hash of the serialized event data, which is
|
@@ -181,6 +181,34 @@ module Nostr
|
|
181
181
|
crypto.sign_event(self, private_key)
|
182
182
|
end
|
183
183
|
|
184
|
+
# Verifies if the signature of the event is valid. A valid signature means that the event was signed by the owner
|
185
|
+
#
|
186
|
+
# @api public
|
187
|
+
#
|
188
|
+
# @example Verifying the signature of an event
|
189
|
+
# event = Nostr::Event.new(
|
190
|
+
# id: '90b75b78daf883ae57fbcc414d43faa028560b3211ee58e4ea82bf395bb82042',
|
191
|
+
# pubkey: Nostr::PublicKey.new('2d7661527d573cc8e84f665fa971dd969ba51e2526df00c149ff8e40a58f9558'),
|
192
|
+
# created_at: 1667422587,
|
193
|
+
# kind: Nostr::EventKind::TEXT_NOTE,
|
194
|
+
# content: 'Your feedback is appreciated, now pay $8',
|
195
|
+
# sig: '32f18adebe942e19b171c1c7d2fb27ce794dfea4155e289dca7952b43ed1ec39' \
|
196
|
+
# '1d3dc198ba2761bc6d40c737a6eaf4edcc8963acabd3bfcebd04f16637025bdc'
|
197
|
+
# )
|
198
|
+
#
|
199
|
+
# event.verify_signature # => true
|
200
|
+
#
|
201
|
+
# @return [Boolean] Whether the signature is valid or not.
|
202
|
+
#
|
203
|
+
def verify_signature
|
204
|
+
crypto = Crypto.new
|
205
|
+
|
206
|
+
return false if id.nil? || pubkey.nil?
|
207
|
+
return false if sig.nil? # FIXME: See https://github.com/soutaro/steep/issues/1079
|
208
|
+
|
209
|
+
crypto.valid_sig?(id, pubkey, sig)
|
210
|
+
end
|
211
|
+
|
184
212
|
# Serializes the event, to obtain a SHA256 digest of it
|
185
213
|
#
|
186
214
|
# @api public
|
data/lib/nostr/event_kind.rb
CHANGED
@@ -21,6 +21,7 @@ module Nostr
|
|
21
21
|
# The content is set to the URL (e.g., wss://somerelay.com) of a relay the event creator wants to
|
22
22
|
# recommend to its followers.
|
23
23
|
#
|
24
|
+
# @deprecated This event kind was removed in https://github.com/nostr-protocol/nips/pull/703/files#diff-39307f1617417657ee9874be314f13aabdc74401b124d0afe8217f2919c9c7d8L105
|
24
25
|
# @return [Integer]
|
25
26
|
#
|
26
27
|
RECOMMEND_SERVER = 2
|
@@ -11,19 +11,19 @@ module Nostr
|
|
11
11
|
# @api public
|
12
12
|
#
|
13
13
|
# @example Instantiating a new encrypted direct message
|
14
|
-
#
|
14
|
+
# Nostr::Events::EncryptedDirectMessage.new(
|
15
15
|
# sender_private_key: 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460',
|
16
16
|
# recipient_public_key: '48df4af6e240ac5f7c5de89bf5941b249880be0e87d03685b178ccb1a315f52e',
|
17
17
|
# plain_text: 'Your feedback is appreciated, now pay $8',
|
18
|
-
#
|
18
|
+
# )
|
19
19
|
#
|
20
20
|
# @example Instantiating a new encrypted direct message that references a previous direct message
|
21
|
-
#
|
21
|
+
# Nostr::Events::EncryptedDirectMessage.new(
|
22
22
|
# sender_private_key: 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460',
|
23
23
|
# recipient_public_key: '48df4af6e240ac5f7c5de89bf5941b249880be0e87d03685b178ccb1a315f52e',
|
24
24
|
# plain_text: 'Your feedback is appreciated, now pay $8',
|
25
25
|
# previous_direct_message: 'ccf9fdf3e1466d7c20969c71ec98defcf5f54aee088513e1b73ccb7bd770d460'
|
26
|
-
#
|
26
|
+
# )
|
27
27
|
#
|
28
28
|
# @param plain_text [String] The +content+ of the encrypted message.
|
29
29
|
# @param sender_private_key [PrivateKey] 32-bytes hex-encoded private key of the message's author.
|
data/lib/nostr/filter.rb
CHANGED
@@ -133,13 +133,16 @@ module Nostr
|
|
133
133
|
# @api public
|
134
134
|
#
|
135
135
|
# @example
|
136
|
-
# filter.to_h # =>
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
#
|
142
|
-
#
|
136
|
+
# filter.to_h # =>
|
137
|
+
# {
|
138
|
+
# ids: ['c24881c305c5cfb7c1168be7e9b0e150'],
|
139
|
+
# authors: ['000000001c5c45196786e79f83d21fe801549fdc98e2c26f96dcef068a5dbcd7'],
|
140
|
+
# kinds: [0, 1, 2],
|
141
|
+
# '#e': ['7bdb422f254194ae4bb86d354c0bd5a888fce233ffc77dceb3e844ceec1fcfb2'],
|
142
|
+
# '#p': ['000000001c5c45196786e79f83d21fe801549fdc98e2c26f96dcef068a5dbcd7'],
|
143
|
+
# since: 1230981305,
|
144
|
+
# until: 1292190341
|
145
|
+
# }
|
143
146
|
#
|
144
147
|
# @return [Hash] The filter as a hash.
|
145
148
|
#
|
data/lib/nostr/key.rb
CHANGED
@@ -18,14 +18,14 @@ module Nostr
|
|
18
18
|
#
|
19
19
|
LENGTH = 64
|
20
20
|
|
21
|
-
# Instantiates a new key. Can't be used directly because this is an abstract class. Raises a +
|
21
|
+
# Instantiates a new key. Can't be used directly because this is an abstract class. Raises a +KeyValidationError+
|
22
22
|
#
|
23
23
|
# @see Nostr::PrivateKey
|
24
24
|
# @see Nostr::PublicKey
|
25
25
|
#
|
26
26
|
# @param [String] hex_value Hex-encoded value of the key
|
27
27
|
#
|
28
|
-
# @raise [
|
28
|
+
# @raise [KeyValidationError]
|
29
29
|
#
|
30
30
|
def initialize(hex_value)
|
31
31
|
validate_hex_value(hex_value)
|
data/lib/nostr/key_pair.rb
CHANGED
@@ -38,8 +38,8 @@ module Nostr
|
|
38
38
|
# @param private_key [PrivateKey] 32-bytes hex-encoded private key.
|
39
39
|
# @param public_key [PublicKey] 32-bytes hex-encoded public key.
|
40
40
|
#
|
41
|
-
# @raise ArgumentError when the private key is not a
|
42
|
-
# @raise ArgumentError when the public key is not a
|
41
|
+
# @raise ArgumentError when the private key is not a {PrivateKey}
|
42
|
+
# @raise ArgumentError when the public key is not a {PublicKey}
|
43
43
|
#
|
44
44
|
def initialize(private_key:, public_key:)
|
45
45
|
validate_keys(private_key, public_key)
|
@@ -48,6 +48,30 @@ module Nostr
|
|
48
48
|
@public_key = public_key
|
49
49
|
end
|
50
50
|
|
51
|
+
# Allows array destructuring of the KeyPair, enabling the extraction of +PrivateKey+ and +PublicKey+ separately
|
52
|
+
#
|
53
|
+
# @api public
|
54
|
+
#
|
55
|
+
# @example Implicit usage of `to_ary` for destructuring
|
56
|
+
# keypair = Nostr::KeyPair.new(
|
57
|
+
# private_key: Nostr::PrivateKey.new('7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d'),
|
58
|
+
# public_key: Nostr::PublicKey.new('15678d8fbc126fa326fac536acd5a6dcb5ef64b3d939abe31d6830cba6cd26d6'),
|
59
|
+
# )
|
60
|
+
# # The `to_ary` method can be implicitly used for array destructuring:
|
61
|
+
# private_key, public_key = keypair
|
62
|
+
# # Now `private_key` and `public_key` hold the respective values.
|
63
|
+
#
|
64
|
+
# @example Explicit usage of `to_ary`
|
65
|
+
# array_representation = keypair.to_ary
|
66
|
+
# # array_representation is now an array: [PrivateKey, PublicKey]
|
67
|
+
# # where PrivateKey and PublicKey are the respective objects.
|
68
|
+
#
|
69
|
+
# @return [Array<PrivateKey, PublicKey>] An array containing the {PrivateKey} and {PublicKey} in that order
|
70
|
+
#
|
71
|
+
def to_ary
|
72
|
+
[private_key, public_key]
|
73
|
+
end
|
74
|
+
|
51
75
|
private
|
52
76
|
|
53
77
|
# Validates the keys
|
data/lib/nostr/keygen.rb
CHANGED
@@ -22,7 +22,7 @@ module Nostr
|
|
22
22
|
# @api public
|
23
23
|
#
|
24
24
|
# @example
|
25
|
-
# keypair = keygen.
|
25
|
+
# keypair = keygen.generate_key_pair
|
26
26
|
# keypair # #<Nostr::KeyPair:0x0000000107bd3550
|
27
27
|
# @private_key="893c4cc8088924796b41dc788f7e2f746734497010b1a9f005c1faad7074b900",
|
28
28
|
# @public_key="2d7661527d573cc8e84f665fa971dd969ba51e2526df00c149ff8e40a58f9558">
|
@@ -0,0 +1,67 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Nostr
|
4
|
+
# 64-bytes lowercase hex of the signature of the sha256 hash of the serialized event data,
|
5
|
+
# which is the same as the "id" field
|
6
|
+
class Signature < String
|
7
|
+
# The regular expression for hexadecimal lowercase characters
|
8
|
+
#
|
9
|
+
# @return [Regexp] The regular expression for hexadecimal lowercase characters
|
10
|
+
#
|
11
|
+
FORMAT = /^[a-f0-9]+$/
|
12
|
+
|
13
|
+
# The length of the signature in hex format
|
14
|
+
#
|
15
|
+
# @return [Integer] The length of the signature in hex format
|
16
|
+
#
|
17
|
+
LENGTH = 128
|
18
|
+
|
19
|
+
# Instantiates a new Signature
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
#
|
23
|
+
# @example Instantiating a new signature
|
24
|
+
# Nostr::Signature.new(
|
25
|
+
# 'f418c97b50cc68227e82f4f3a79d79eb2b7a0fa517859c86e1a8fa91e3741b7f' \
|
26
|
+
# '06e070c44129227b83fcbe93cecb02a346804a4080ce47685ecad60ab4f5f128'
|
27
|
+
# )
|
28
|
+
#
|
29
|
+
# @param [String] hex_value Hex-encoded value of the signature
|
30
|
+
#
|
31
|
+
# @raise [SignatureValidationError]
|
32
|
+
#
|
33
|
+
def initialize(hex_value)
|
34
|
+
validate(hex_value)
|
35
|
+
|
36
|
+
super(hex_value)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Hex-encoded value of the signature
|
42
|
+
#
|
43
|
+
# @api private
|
44
|
+
#
|
45
|
+
# @return [String] hex_value Hex-encoded value of the signature
|
46
|
+
#
|
47
|
+
attr_reader :hex_value
|
48
|
+
|
49
|
+
# Validates the hex value of the signature
|
50
|
+
#
|
51
|
+
# @api private
|
52
|
+
#
|
53
|
+
# @param [String] hex_value The signature in hex format
|
54
|
+
#
|
55
|
+
# @raise InvalidSignatureTypeError when the signature is not a string
|
56
|
+
# @raise InvalidSignatureLengthError when the signature's length is not 128 characters
|
57
|
+
# @raise InvalidSignatureFormatError when the signature is in an invalid format
|
58
|
+
#
|
59
|
+
# @return [void]
|
60
|
+
#
|
61
|
+
def validate(hex_value)
|
62
|
+
raise InvalidSignatureTypeError unless hex_value.is_a?(String)
|
63
|
+
raise InvalidSignatureLengthError unless hex_value.size == LENGTH
|
64
|
+
raise InvalidSignatureFormatError unless hex_value.match(FORMAT)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/nostr/version.rb
CHANGED
data/lib/nostr.rb
CHANGED
@@ -12,6 +12,7 @@ require_relative 'nostr/relay'
|
|
12
12
|
require_relative 'nostr/relay_message_type'
|
13
13
|
require_relative 'nostr/key_pair'
|
14
14
|
require_relative 'nostr/event_kind'
|
15
|
+
require_relative 'nostr/signature'
|
15
16
|
require_relative 'nostr/event'
|
16
17
|
require_relative 'nostr/events/encrypted_direct_message'
|
17
18
|
require_relative 'nostr/client'
|
data/nostr.gemspec
CHANGED
@@ -12,7 +12,7 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.description = 'Client and relay implementation of the Nostr protocol.'
|
13
13
|
spec.homepage = 'https://nostr-ruby.com/'
|
14
14
|
spec.license = 'MIT'
|
15
|
-
spec.required_ruby_version = '>= 3.
|
15
|
+
spec.required_ruby_version = '>= 3.3.0'
|
16
16
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
17
17
|
|
18
18
|
spec.metadata['homepage_uri'] = spec.homepage
|
@@ -32,29 +32,29 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.require_paths = ['lib']
|
33
33
|
|
34
34
|
spec.add_dependency 'bech32', '~> 1.4'
|
35
|
-
spec.add_dependency 'bip-schnorr', '~> 0.
|
35
|
+
spec.add_dependency 'bip-schnorr', '~> 0.7'
|
36
36
|
spec.add_dependency 'ecdsa', '~> 1.2'
|
37
37
|
spec.add_dependency 'event_emitter', '~> 0.2'
|
38
38
|
spec.add_dependency 'faye-websocket', '~> 0.11'
|
39
39
|
spec.add_dependency 'json', '~> 2.6'
|
40
40
|
|
41
41
|
spec.add_development_dependency 'bundler-audit', '~> 0.9'
|
42
|
-
spec.add_development_dependency 'dotenv', '~>
|
42
|
+
spec.add_development_dependency 'dotenv', '~> 3.1'
|
43
43
|
spec.add_development_dependency 'guard', '~> 2.18'
|
44
44
|
spec.add_development_dependency 'guard-bundler', '~> 3.0'
|
45
45
|
spec.add_development_dependency 'guard-bundler-audit', '~> 0.1'
|
46
46
|
spec.add_development_dependency 'guard-rspec', '~> 4.7'
|
47
47
|
spec.add_development_dependency 'guard-rubocop', '~> 1.5'
|
48
|
-
spec.add_development_dependency 'overcommit', '~> 0.
|
48
|
+
spec.add_development_dependency 'overcommit', '~> 0.63'
|
49
49
|
spec.add_development_dependency 'pry', '~> 0.14'
|
50
50
|
spec.add_development_dependency 'puma', '~> 6.4'
|
51
51
|
spec.add_development_dependency 'rack', '~> 3.0'
|
52
52
|
spec.add_development_dependency 'rake', '~> 13.1'
|
53
|
-
spec.add_development_dependency 'rbs', '~> 3.
|
54
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
55
|
-
spec.add_development_dependency 'rubocop', '~> 1.
|
53
|
+
spec.add_development_dependency 'rbs', '~> 3.4'
|
54
|
+
spec.add_development_dependency 'rspec', '~> 3.13'
|
55
|
+
spec.add_development_dependency 'rubocop', '~> 1.62'
|
56
56
|
spec.add_development_dependency 'rubocop-rake', '~> 0.6'
|
57
|
-
spec.add_development_dependency 'rubocop-rspec', '2.
|
57
|
+
spec.add_development_dependency 'rubocop-rspec', '2.27'
|
58
58
|
spec.add_development_dependency 'simplecov', '= 0.17'
|
59
59
|
spec.add_development_dependency 'simplecov-console', '~> 0.9'
|
60
60
|
spec.add_development_dependency 'steep', '~> 1.6'
|
data/sig/nostr/crypto.rbs
CHANGED
@@ -7,6 +7,9 @@ module Nostr
|
|
7
7
|
def encrypt_text: (PrivateKey, PublicKey, String) -> String
|
8
8
|
def decrypt_text: (PrivateKey, PublicKey, String) -> String
|
9
9
|
def sign_event: (Event, PrivateKey) -> Event
|
10
|
+
def sign_message: (String, PrivateKey) -> Signature
|
11
|
+
def valid_sig?: (String, PublicKey, Signature) -> bool
|
12
|
+
def check_sig!: (String, PublicKey, Signature) -> bool
|
10
13
|
|
11
14
|
private
|
12
15
|
|
data/sig/nostr/event.rbs
CHANGED
@@ -5,8 +5,8 @@ module Nostr
|
|
5
5
|
attr_reader kind: Integer
|
6
6
|
attr_reader tags: Array[Array[String]]
|
7
7
|
attr_reader content: String
|
8
|
-
attr_accessor id: String
|
9
|
-
attr_accessor sig:
|
8
|
+
attr_accessor id: String?
|
9
|
+
attr_accessor sig: Signature?
|
10
10
|
|
11
11
|
def initialize: (
|
12
12
|
pubkey: PublicKey,
|
@@ -14,24 +14,25 @@ module Nostr
|
|
14
14
|
content: String,
|
15
15
|
?created_at: Integer,
|
16
16
|
?tags: Array[Array[String]],
|
17
|
-
?id: String
|
18
|
-
?sig:
|
17
|
+
?id: String?,
|
18
|
+
?sig: Signature?
|
19
19
|
) -> void
|
20
20
|
|
21
21
|
def serialize: -> [Integer, String, Integer, Integer, Array[Array[String]], String]
|
22
22
|
|
23
23
|
def to_h: -> {
|
24
|
-
id: String
|
24
|
+
id: String?,
|
25
25
|
pubkey: String,
|
26
26
|
created_at: Integer,
|
27
27
|
kind: Integer,
|
28
28
|
tags: Array[Array[String]],
|
29
29
|
content: String,
|
30
|
-
sig: String
|
30
|
+
sig: String?
|
31
31
|
}
|
32
32
|
def ==: (Event other) -> bool
|
33
33
|
|
34
34
|
def sign:(PrivateKey) -> Event
|
35
|
+
def verify_signature: -> bool
|
35
36
|
|
36
37
|
def add_event_reference: (String) -> Array[Array[String]]
|
37
38
|
def add_pubkey_reference: (PublicKey) -> Array[Array[String]]
|
data/sig/nostr/key_pair.rbs
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
# Added only to satisfy the Steep requirements. Not 100% reliable.
|
2
|
+
module Schnorr
|
3
|
+
class InvalidSignatureError < StandardError
|
4
|
+
end
|
5
|
+
|
6
|
+
class Signature
|
7
|
+
attr_reader r: Integer
|
8
|
+
attr_reader s: Integer
|
9
|
+
|
10
|
+
def self.decode: (String string) -> Signature
|
11
|
+
|
12
|
+
def initialize: (Integer r, Integer s) -> void
|
13
|
+
def encode: -> String
|
14
|
+
def ==: (untyped other) -> bool
|
15
|
+
end
|
16
|
+
end
|
data/sig/vendor/schnorr.rbs
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# Added only to satisfy the Steep requirements. Not 100% reliable.
|
2
2
|
module Schnorr
|
3
|
-
def self.sign: (String message, String private_key, ?String aux_rand) ->
|
3
|
+
def self.sign: (String message, String private_key, ?String aux_rand) -> Signature
|
4
|
+
def self.valid_sig?: (String message, String public_key, String signature) -> bool
|
5
|
+
def self.check_sig!: (String message, String public_key, String signature) -> bool
|
4
6
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nostr
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wilson Silva
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-03-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bech32
|
@@ -30,14 +30,14 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
33
|
+
version: '0.7'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0.
|
40
|
+
version: '0.7'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: ecdsa
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '3.1'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '3.1'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: guard
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -198,14 +198,14 @@ dependencies:
|
|
198
198
|
requirements:
|
199
199
|
- - "~>"
|
200
200
|
- !ruby/object:Gem::Version
|
201
|
-
version: '0.
|
201
|
+
version: '0.63'
|
202
202
|
type: :development
|
203
203
|
prerelease: false
|
204
204
|
version_requirements: !ruby/object:Gem::Requirement
|
205
205
|
requirements:
|
206
206
|
- - "~>"
|
207
207
|
- !ruby/object:Gem::Version
|
208
|
-
version: '0.
|
208
|
+
version: '0.63'
|
209
209
|
- !ruby/object:Gem::Dependency
|
210
210
|
name: pry
|
211
211
|
requirement: !ruby/object:Gem::Requirement
|
@@ -268,42 +268,42 @@ dependencies:
|
|
268
268
|
requirements:
|
269
269
|
- - "~>"
|
270
270
|
- !ruby/object:Gem::Version
|
271
|
-
version: '3.
|
271
|
+
version: '3.4'
|
272
272
|
type: :development
|
273
273
|
prerelease: false
|
274
274
|
version_requirements: !ruby/object:Gem::Requirement
|
275
275
|
requirements:
|
276
276
|
- - "~>"
|
277
277
|
- !ruby/object:Gem::Version
|
278
|
-
version: '3.
|
278
|
+
version: '3.4'
|
279
279
|
- !ruby/object:Gem::Dependency
|
280
280
|
name: rspec
|
281
281
|
requirement: !ruby/object:Gem::Requirement
|
282
282
|
requirements:
|
283
283
|
- - "~>"
|
284
284
|
- !ruby/object:Gem::Version
|
285
|
-
version: '3.
|
285
|
+
version: '3.13'
|
286
286
|
type: :development
|
287
287
|
prerelease: false
|
288
288
|
version_requirements: !ruby/object:Gem::Requirement
|
289
289
|
requirements:
|
290
290
|
- - "~>"
|
291
291
|
- !ruby/object:Gem::Version
|
292
|
-
version: '3.
|
292
|
+
version: '3.13'
|
293
293
|
- !ruby/object:Gem::Dependency
|
294
294
|
name: rubocop
|
295
295
|
requirement: !ruby/object:Gem::Requirement
|
296
296
|
requirements:
|
297
297
|
- - "~>"
|
298
298
|
- !ruby/object:Gem::Version
|
299
|
-
version: '1.
|
299
|
+
version: '1.62'
|
300
300
|
type: :development
|
301
301
|
prerelease: false
|
302
302
|
version_requirements: !ruby/object:Gem::Requirement
|
303
303
|
requirements:
|
304
304
|
- - "~>"
|
305
305
|
- !ruby/object:Gem::Version
|
306
|
-
version: '1.
|
306
|
+
version: '1.62'
|
307
307
|
- !ruby/object:Gem::Dependency
|
308
308
|
name: rubocop-rake
|
309
309
|
requirement: !ruby/object:Gem::Requirement
|
@@ -324,14 +324,14 @@ dependencies:
|
|
324
324
|
requirements:
|
325
325
|
- - '='
|
326
326
|
- !ruby/object:Gem::Version
|
327
|
-
version: '2.
|
327
|
+
version: '2.27'
|
328
328
|
type: :development
|
329
329
|
prerelease: false
|
330
330
|
version_requirements: !ruby/object:Gem::Requirement
|
331
331
|
requirements:
|
332
332
|
- - '='
|
333
333
|
- !ruby/object:Gem::Version
|
334
|
-
version: '2.
|
334
|
+
version: '2.27'
|
335
335
|
- !ruby/object:Gem::Dependency
|
336
336
|
name: simplecov
|
337
337
|
requirement: !ruby/object:Gem::Requirement
|
@@ -437,6 +437,7 @@ executables: []
|
|
437
437
|
extensions: []
|
438
438
|
extra_rdoc_files: []
|
439
439
|
files:
|
440
|
+
- ".adr-dir"
|
440
441
|
- ".editorconfig"
|
441
442
|
- ".overcommit.yml"
|
442
443
|
- ".rspec"
|
@@ -452,12 +453,16 @@ files:
|
|
452
453
|
- README.md
|
453
454
|
- Rakefile
|
454
455
|
- Steepfile
|
456
|
+
- adr/0001-record-architecture-decisions.md
|
457
|
+
- adr/0002-introduction-of-signature-class.md
|
455
458
|
- docs/.gitignore
|
456
459
|
- docs/.vitepress/config.mjs
|
457
460
|
- docs/README.md
|
458
461
|
- docs/api-examples.md
|
459
462
|
- docs/bun.lockb
|
460
463
|
- docs/common-use-cases/bech32-encoding-and-decoding-(NIP-19).md
|
464
|
+
- docs/common-use-cases/signing-and-verifying-events.md
|
465
|
+
- docs/common-use-cases/signing-and-verifying-messages.md
|
461
466
|
- docs/core/client.md
|
462
467
|
- docs/core/keys.md
|
463
468
|
- docs/core/user.md
|
@@ -491,7 +496,11 @@ files:
|
|
491
496
|
- lib/nostr/errors/invalid_key_format_error.rb
|
492
497
|
- lib/nostr/errors/invalid_key_length_error.rb
|
493
498
|
- lib/nostr/errors/invalid_key_type_error.rb
|
499
|
+
- lib/nostr/errors/invalid_signature_format_error.rb
|
500
|
+
- lib/nostr/errors/invalid_signature_length_error.rb
|
501
|
+
- lib/nostr/errors/invalid_signature_type_error.rb
|
494
502
|
- lib/nostr/errors/key_validation_error.rb
|
503
|
+
- lib/nostr/errors/signature_validation_error.rb
|
495
504
|
- lib/nostr/event.rb
|
496
505
|
- lib/nostr/event_kind.rb
|
497
506
|
- lib/nostr/events/encrypted_direct_message.rb
|
@@ -503,6 +512,7 @@ files:
|
|
503
512
|
- lib/nostr/public_key.rb
|
504
513
|
- lib/nostr/relay.rb
|
505
514
|
- lib/nostr/relay_message_type.rb
|
515
|
+
- lib/nostr/signature.rb
|
506
516
|
- lib/nostr/subscription.rb
|
507
517
|
- lib/nostr/user.rb
|
508
518
|
- lib/nostr/version.rb
|
@@ -517,7 +527,11 @@ files:
|
|
517
527
|
- sig/nostr/errors/invalid_key_format_error.rbs
|
518
528
|
- sig/nostr/errors/invalid_key_length_error.rbs
|
519
529
|
- sig/nostr/errors/invalid_key_type_error.rbs
|
530
|
+
- sig/nostr/errors/invalid_signature_format_error.rbs
|
531
|
+
- sig/nostr/errors/invalid_signature_length_error.rbs
|
532
|
+
- sig/nostr/errors/invalid_signature_type_error.rbs
|
520
533
|
- sig/nostr/errors/key_validation_error.rbs
|
534
|
+
- sig/nostr/errors/signature_validation_error.rbs
|
521
535
|
- sig/nostr/event.rbs
|
522
536
|
- sig/nostr/event_kind.rbs
|
523
537
|
- sig/nostr/events/encrypted_direct_message.rbs
|
@@ -529,6 +543,7 @@ files:
|
|
529
543
|
- sig/nostr/public_key.rbs
|
530
544
|
- sig/nostr/relay.rbs
|
531
545
|
- sig/nostr/relay_message_type.rbs
|
546
|
+
- sig/nostr/signature.rbs
|
532
547
|
- sig/nostr/subscription.rbs
|
533
548
|
- sig/nostr/user.rbs
|
534
549
|
- sig/vendor/bech32.rbs
|
@@ -543,6 +558,7 @@ files:
|
|
543
558
|
- sig/vendor/faye/websocket/api.rbs
|
544
559
|
- sig/vendor/faye/websocket/client.rbs
|
545
560
|
- sig/vendor/schnorr.rbs
|
561
|
+
- sig/vendor/schnorr/signature.rbs
|
546
562
|
homepage: https://nostr-ruby.com/
|
547
563
|
licenses:
|
548
564
|
- MIT
|
@@ -559,14 +575,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
559
575
|
requirements:
|
560
576
|
- - ">="
|
561
577
|
- !ruby/object:Gem::Version
|
562
|
-
version: 3.
|
578
|
+
version: 3.3.0
|
563
579
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
564
580
|
requirements:
|
565
581
|
- - ">="
|
566
582
|
- !ruby/object:Gem::Version
|
567
583
|
version: '0'
|
568
584
|
requirements: []
|
569
|
-
rubygems_version: 3.
|
585
|
+
rubygems_version: 3.5.6
|
570
586
|
signing_key:
|
571
587
|
specification_version: 4
|
572
588
|
summary: Client and relay implementation of the Nostr protocol.
|