Class: Nostr::Crypto

Inherits:
Object
  • Object
show all
Defined in:
lib/nostr/crypto.rb

Overview

Performs cryptographic operations.

Constant Summary collapse

BN_BASE =

Numeric base of the OpenSSL big number used in an event content’s encryption.

Returns:

  • (Integer)
16
CIPHER_CURVE =

Name of the cipher curve used in an event content’s encryption.

Returns:

  • (String)
'secp256k1'
CIPHER_ALGORITHM =

Name of the cipher algorithm used in an event content’s encryption.

Returns:

  • (String)
'aes-256-cbc'

Instance Method Summary collapse

Instance Method Details

#check_sig!(message, public_key, signature) ⇒ Boolean

Verifies the given Signature and raises an Schnorr::InvalidSignatureError if it is invalid

Examples:

Checking a signature

public_key = Nostr::PublicKey.new('15678d8fbc126fa326fac536acd5a6dcb5ef64b3d939abe31d6830cba6cd26d6')
private_key = Nostr::PrivateKey.new('7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d')
message = 'Viva la libertad carajo'
crypto = Nostr::Crypto.new
signature = crypto.sign_message(message, private_key)
valid = crypto.valid_sig?(message, public_key, signature)
valid # => true

Parameters:

  • message (String)

    A message to be signed with binary format.

  • public_key (PublicKey)

    The public key with binary format.

  • signature (Signature)

    The signature with binary format.

Returns:

  • (Boolean)

    whether signature is valid.

Raises:

  • (Schnorr::InvalidSignatureError)

    if the signature is invalid.

See Also:



175
176
177
178
# File 'lib/nostr/crypto.rb', line 175

def check_sig!(message, public_key, signature)
  signature = Schnorr::Signature.decode([signature].pack('H*'))
  Schnorr.check_sig!([message].pack('H*'), [public_key].pack('H*'), signature.encode)
end

#decrypt_text(recipient_private_key, sender_public_key, encrypted_text) ⇒ String

Decrypts a piece of text

Examples:

Encrypting an event’s content

crypto = Nostr::Crypto.new
encrypted # => "wrYQaHDfpOEvyJELSCg1vzsywmlJTz8NqH03eFW44s8iQs869jtSb26Lr4s23gmY?iv=v38vAJ3LlJAGZxbmWU4qAg=="
decrypted = crypto.decrypt_text(recipient_private_key, sender_public_key, encrypted)

Parameters:

  • sender_public_key (PublicKey)

    32-bytes hex-encoded public key of the message creator.

  • recipient_private_key (PrivateKey)

    32-bytes hex-encoded public key of the recipient.

  • encrypted_text (String)

    The text to be decrypted

Returns:

  • (String)

    Decrypted text.



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/nostr/crypto.rb', line 63

def decrypt_text(recipient_private_key, sender_public_key, encrypted_text)
  base64_encoded_text, iv = encrypted_text.split('?iv=')

  # Ensure iv and base64_encoded_text are not nil
  return '' unless iv && base64_encoded_text

  cipher = OpenSSL::Cipher.new(CIPHER_ALGORITHM).decrypt
  cipher.iv = Base64.decode64(iv)
  cipher.key = compute_shared_key(recipient_private_key, sender_public_key)
  plain_text = cipher.update(Base64.decode64(base64_encoded_text)) + cipher.final
  plain_text.force_encoding('UTF-8')
end

#encrypt_text(sender_private_key, recipient_public_key, plain_text) ⇒ String

Encrypts a piece of text

Examples:

Encrypting an event’s content

crypto = Nostr::Crypto.new
encrypted = crypto.encrypt_text(sender_private_key, recipient_public_key, 'Feedback appreciated. Now pay $8')
encrypted # => "wrYQaHDfpOEvyJELSCg1vzsywmlJTz8NqH03eFW44s8iQs869jtSb26Lr4s23gmY?iv=v38vAJ3LlJAGZxbmWU4qAg=="

Parameters:

  • sender_private_key (PrivateKey)

    32-bytes hex-encoded private key of the creator.

  • recipient_public_key (PublicKey)

    32-bytes hex-encoded public key of the recipient.

  • plain_text (String)

    The text to be encrypted

Returns:

  • (String)

    Encrypted text.



39
40
41
42
43
44
45
46
# File 'lib/nostr/crypto.rb', line 39

def encrypt_text(sender_private_key, recipient_public_key, plain_text)
  cipher = OpenSSL::Cipher.new(CIPHER_ALGORITHM).encrypt
  cipher.iv = iv = cipher.random_iv
  cipher.key = compute_shared_key(sender_private_key, recipient_public_key)
  encrypted_text = cipher.update(plain_text) + cipher.final
  encrypted_text = "#{Base64.encode64(encrypted_text)}?iv=#{Base64.encode64(iv)}"
  encrypted_text.gsub("\n", '')
end

#sign_event(event, private_key) ⇒ Event

Uses the private key to generate an event id and sign the event

Examples:

Signing an event

crypto = Nostr::Crypto.new
crypto.sign(event, private_key)
event.id # => an id
event.sig # => a signature

Parameters:

  • event (Event)

    The event to be signed

  • private_key (PrivateKey)

    32-bytes hex-encoded private key.

Returns:

  • (Event)

    An unsigned event.



91
92
93
94
95
96
97
98
99
# File 'lib/nostr/crypto.rb', line 91

def sign_event(event, private_key)
  event_digest = hash_event(event)
  signature = sign_message(event_digest, private_key)

  event.id = event_digest
  event.sig = signature

  event
end

#sign_message(message, private_key) ⇒ Signature

Signs a message using the Schnorr signature algorithm

Examples:

Signing a message

crypto = Nostr::Crypto.new
message = 'Viva la libertad carajo'
private_key = Nostr::PrivateKey.new('7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d')
signature = crypto.sign_message(message, private_key)
signature # => 'b2115694a576f5bdcebf8c0951a3c7adcfbdb17b11cb9e6d6b7017691138bc6' \
  '38fee642a7bd26f71b313a7057181294198900a9770d1435e43f182acf3d34c26'

Parameters:

  • message (String)

    The message to be signed

  • private_key (PrivateKey)

    The private key used for signing

Returns:

  • (Signature)

    A signature object containing the signature as a 64-byte hexadecimal string.



118
119
120
121
122
123
124
# File 'lib/nostr/crypto.rb', line 118

def sign_message(message, private_key)
  hex_private_key = Array(private_key).pack('H*')
  hex_message     = Array(message).pack('H*')
  hex_signature   = Schnorr.sign(hex_message, hex_private_key).encode.unpack1('H*')

  Signature.new(hex_signature.to_s)
end

#valid_sig?(message, public_key, signature) ⇒ Boolean

Verifies the given Signature and returns true if it is valid

Examples:

Checking a signature

public_key = Nostr::PublicKey.new('15678d8fbc126fa326fac536acd5a6dcb5ef64b3d939abe31d6830cba6cd26d6')
private_key = Nostr::PrivateKey.new('7d1e4219a5e7d8342654c675085bfbdee143f0eb0f0921f5541ef1705a2b407d')
message = 'Viva la libertad carajo'
crypto = Nostr::Crypto.new
signature = crypto.sign_message(message, private_key)
valid = crypto.valid_sig?(message, public_key, signature)
valid # => true

Parameters:

  • message (String)

    A message to be signed with binary format.

  • public_key (PublicKey)

    The public key with binary format.

  • signature (Signature)

    The signature with binary format.

Returns:

  • (Boolean)

    whether signature is valid.

See Also:



147
148
149
150
# File 'lib/nostr/crypto.rb', line 147

def valid_sig?(message, public_key, signature)
  signature = Schnorr::Signature.decode([signature].pack('H*'))
  Schnorr.valid_sig?([message].pack('H*'), [public_key].pack('H*'), signature.encode)
end