Class: CoinOp::Crypto::PassphraseBox

Inherits:
Object
  • Object
show all
Extended by:
Encodings
Includes:
Encodings
Defined in:
lib/coin-op/crypto.rb

Overview

A wrapper for NaCl’s Secret Box, taking a user-supplied passphrase and deriving a secret key, rather than using a (far more secure) randomly generated secret key.

NaCl Secret Box provides a high level interface for authenticated symmetric encryption. When creating the box, you must supply a key. When using the box to encrypt, you must supply a random nonce. Nonces must never be re-used.

Secret Box decryption requires the ciphertext and the nonce used to create it.

The PassphraseBox class takes a passphrase, rather than a randomly generated key. It uses PBKDF2 to generate a key that, while not random, is somewhat resistant to brute force attacks. Great care should still be taken to avoid passphrases that are subject to dictionary attacks.

Constant Summary collapse

ITERATIONS =

PBKDF2 work factor

100_000

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Encodings

base58, decode_base58, decode_hex, hex

Constructor Details

#initialize(passphrase, salt = nil, iterations = nil) ⇒ PassphraseBox

Initialize with an existing salt and iterations to allow decryption. Otherwise, creates new values for these, meaning it creates an entirely new secret-box.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/coin-op/crypto.rb', line 57

def initialize(passphrase, salt=nil, iterations=nil)
  @salt = salt || RbNaCl::Random.random_bytes(16)
  @iterations = iterations || ITERATIONS

  key = OpenSSL::PKCS5.pbkdf2_hmac_sha1(
    passphrase, @salt,
    # TODO: decide on a very safe work factor
    # https://www.owasp.org/index.php/Password_Storage_Cheat_Sheet
    #
    @iterations, # number of iterations
    32      # key length in bytes
  )
  @box = RbNaCl::SecretBox.new(key)
end

Instance Attribute Details

#saltObject (readonly)

Returns the value of attribute salt.



52
53
54
# File 'lib/coin-op/crypto.rb', line 52

def salt
  @salt
end

Class Method Details

.decrypt(passphrase, hash) ⇒ Object

PassphraseBox.decrypt “my great password”,

:salt => salt, :nonce => nonce, :ciphertext => ciphertext


45
46
47
48
49
50
# File 'lib/coin-op/crypto.rb', line 45

def self.decrypt(passphrase, hash)
  salt, nonce, ciphertext =
    hash.values_at(:salt, :nonce, :ciphertext).map {|s| decode_hex(s) }
  box = self.new(passphrase, salt, hash[:iterations])
  box.decrypt(nonce, ciphertext)
end

.encrypt(passphrase, plaintext) ⇒ Object

Given passphrase and plaintext as strings, returns a Hash containing the ciphertext and other values needed for later decryption. Binary values are encoded as hexadecimal strings.



37
38
39
40
# File 'lib/coin-op/crypto.rb', line 37

def self.encrypt(passphrase, plaintext)
  box = self.new(passphrase)
  box.encrypt(plaintext)
end

Instance Method Details

#decrypt(nonce, ciphertext) ⇒ Object



83
84
85
# File 'lib/coin-op/crypto.rb', line 83

def decrypt(nonce, ciphertext)
  @box.decrypt(nonce, ciphertext)
end

#encrypt(plaintext) ⇒ Object



72
73
74
75
76
77
78
79
80
81
# File 'lib/coin-op/crypto.rb', line 72

def encrypt(plaintext)
  nonce = RbNaCl::Random.random_bytes(RbNaCl::SecretBox.nonce_bytes)
  ciphertext = @box.encrypt(nonce, plaintext)
  {
    :iterations => @iterations,
    :salt => hex(@salt),
    :nonce => hex(nonce),
    :ciphertext => hex(ciphertext)
  }
end