cryptosphere 0.0.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.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,4 @@
1
+ --color
2
+ --format documentation
3
+ --backtrace
4
+ --default_path spec
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - ruby-head
4
+ # - jruby-19mode not working due to JCE issues
5
+ # - jruby-head
6
+ - rbx-19mode
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source :rubygems
2
+
3
+ gem 'jruby-openssl', :platform => 'jruby'
4
+
5
+ # Specify your gem's dependencies in cryptosphere.gemspec
6
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Tony Arcieri
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,77 @@
1
+ ![Celluloid](https://github.com/tarcieri/cryptosphere/raw/master/logo.png)
2
+ ================
3
+ [![Build Status](http://travis-ci.org/tarcieri/cryptosphere.png)](http://travis-ci.org/tarcieri/cryptosphere)
4
+
5
+ > "I want people to see the truth... regardless of who they are... because
6
+ > without information, you cannot make informed decisions as a public" _-- Bradley Manning_
7
+
8
+ The Cryptosphere is a global peer-to-peer cryptosystem for publishing and
9
+ securely distributing content anonymously with no central point of failure.
10
+ The system is openly federated and anyone can join. To ensure quality service
11
+ and prevent abuse, the Cryptosphere uses an integrated cryptographically
12
+ secure reputation system which provides a distributed web of trust.
13
+
14
+ There are several systems with similar goals to the Cryptosphere, such as
15
+ MNet, Freenet, and Tahoe-LAFS. These systems serve as inspiration for the
16
+ Cryptosphere's design. The Cryptosphere is also heavily influenced by Git, the
17
+ distributed version control system.
18
+
19
+ For more information, please see the [project philosophy][philosophy]
20
+ page in the wiki.
21
+
22
+ Like the Cryptosphere? [Join the Google Group][google group]
23
+ We're also on IRC at #cryptosphere on irc.freenode.net
24
+
25
+ [philosophy]: https://github.com/tarcieri/cryptosphere/wiki/Philosophy
26
+ [google group]: https://groups.google.com/group/cryptosphere
27
+
28
+ ### Is it any good?
29
+
30
+ [Yes.](http://news.ycombinator.com/item?id=3067434)
31
+
32
+ ### Is It "Production Ready™"?
33
+
34
+ No, the Cryptosphere is still in an early development stage, and is not yet
35
+ ready for general usage.
36
+
37
+ Use Cases
38
+ ---------
39
+
40
+ The Cryptosphere provides an encrypted storage system where only users with
41
+ the capability tokens for respective content are able to access it. Unlike
42
+ many other peer to systems, there is no global search system because all
43
+ content in the system is encrypted and therefore unsearchable.
44
+
45
+ This makes the Cryptosphere quite a bit different from many other P2P systems
46
+ which sought to publicize users content. Instead, the Cryptosphere tries to
47
+ keep your content as confidential as possible. This makes it useful for the
48
+ following things:
49
+
50
+ * Secure personal backups
51
+ * File sharing among small groups (ala Dropbox)
52
+ * Secure anonymous encrypted source control
53
+ * Censorship-proof anonymous web hosting
54
+
55
+ Suggested Reading
56
+ -----------------
57
+
58
+ * [Tahoe - The Least-Authority Filesystem (Tahoe-LAFS)](https://tahoe-lafs.org/~zooko/lafs.pdf)
59
+ * [A Distributed Decentralized Information Storage and Retrieval System (Freenet)](http://freenetproject.org/papers/ddisrs.pdf)
60
+ * [Efficient Sharing of Encrypted Data (GNUnet)](http://grothoff.org/christian/esed.pdf)
61
+ * [Samsara: Honor Among Thieves in Peer-to-Peer Storage](http://www.eecs.harvard.edu/~mema/courses/cs264/papers/samsara-sosp2003.pdf)
62
+ * [The Sybil Attack](http://research.microsoft.com/pubs/74220/IPTPS2002.pdf)
63
+ * [A Sybilproof Indirect Reciprocity Mechanism for Peer-to-Peer Networks](http://discovery.ucl.ac.uk/14962/1/14962.pdf)
64
+ * [Incentive-driven QoS in peer-to-peer overlays](http://discovery.ucl.ac.uk/19490/1/19490.pdf)
65
+
66
+ Contributing to the Cryptosphere
67
+ --------------------------------
68
+
69
+ * Fork this repository on github
70
+ * Make your changes and send me a pull request
71
+ * If I like them I'll merge them
72
+
73
+ License
74
+ -------
75
+
76
+ Copyright (c) 2012 Tony Arcieri. Distributed under the MIT License. See
77
+ LICENSE.txt for further details.
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+
4
+ Dir["tasks/**/*.task"].each { |task| load task }
5
+
6
+ task :default => :spec
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require 'cryptosphere'
3
+
4
+ Cryptosphere::CLI.start(ARGV)
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/cryptosphere/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Tony Arcieri"]
6
+ gem.email = ["tony.arcieri@gmail.com"]
7
+ gem.description = "A decentralized globally distributed peer-to-peer data archive"
8
+ gem.summary = "The Cryptosphere is a P2P cryptosystem for publishing and securely distributing content anonymously with no central point of failure"
9
+ gem.homepage = "http://github.com/tarcieri/cryptosphere"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "cryptosphere"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Cryptosphere::VERSION
17
+
18
+ gem.add_dependency "celluloid"
19
+ gem.add_dependency "thor"
20
+ gem.add_dependency "hkdf"
21
+
22
+ gem.add_development_dependency "rake"
23
+ gem.add_development_dependency "rspec"
24
+ end
@@ -0,0 +1,45 @@
1
+ require 'digest/sha2'
2
+ require 'openssl'
3
+ require 'cryptosphere/version'
4
+
5
+ require 'cryptosphere/crypto/asymmetric_cipher'
6
+ require 'cryptosphere/crypto/kdf'
7
+ require 'cryptosphere/crypto/signature_algorithm'
8
+
9
+ require 'cryptosphere/blobs/blob'
10
+ require 'cryptosphere/blobs/tree'
11
+
12
+ require 'cryptosphere/protocol/handshake'
13
+
14
+ require 'cryptosphere/cli'
15
+ require 'cryptosphere/head'
16
+ require 'cryptosphere/identity'
17
+
18
+ module Cryptosphere
19
+ # How large of a key to use for the pubkey cipher
20
+ PUBKEY_SIZE = 2048
21
+
22
+ # Secure random data source
23
+ def random_bytes(size)
24
+ OpenSSL::Random.random_bytes(size)
25
+ end
26
+
27
+ # 256-bit hash function
28
+ def self.hash_function
29
+ Digest::SHA256.new
30
+ end
31
+
32
+ # 256-bit block cipher
33
+ def self.block_cipher
34
+ OpenSSL::Cipher::Cipher.new("aes-256-cbc")
35
+ end
36
+
37
+ # Request to do something we're incapable of
38
+ class CapabilityError < StandardError; end
39
+
40
+ # Signature doesn't match (potential data tampering)
41
+ class InvalidSignatureError < StandardError; end
42
+
43
+ # Implausible timestamps (i.e. ones from the future)
44
+ class InvalidTimestampError < StandardError; end
45
+ end
@@ -0,0 +1,117 @@
1
+ require 'tempfile'
2
+ require 'fileutils'
3
+
4
+ module Cryptosphere
5
+ class Blob
6
+ # Prefix added to the beginning of every Cryptosphere node
7
+ PREFIX = "blob::"
8
+
9
+ attr_reader :id, :key, :path
10
+
11
+ class << self
12
+ attr_reader :path
13
+
14
+ # Configure the Cryptosphere Node store
15
+ def setup(options = {})
16
+ unless options[:root]
17
+ raise ArgumentError, "no :root path given"
18
+ end
19
+
20
+ unless File.directory? options[:root]
21
+ raise ArgumentError, "no such directory: #{options[:root]}"
22
+ end
23
+
24
+ @path = File.expand_path("nodes", options[:root])
25
+ FileUtils.mkdir @path unless File.directory? @path
26
+
27
+ nil
28
+ end
29
+
30
+ # Create a node from a given object
31
+ def [](obj)
32
+ builder = Builder.new
33
+ builder << obj
34
+ builder.finish
35
+ end
36
+ end
37
+
38
+ def initialize(id, key)
39
+ @id, @key = id, key
40
+ @path = File.join(self.class.path, @id)
41
+ end
42
+
43
+ def decrypt
44
+ raise "can't decrypt node without key" unless @key
45
+
46
+ cipher = Cryptosphere.block_cipher
47
+ cipher.decrypt
48
+ cipher.key = @key[0...32]
49
+ cipher.iv = @key[32...64]
50
+
51
+ output = ''
52
+
53
+ File.open(@path, 'r') do |file|
54
+ while data = file.read(4096)
55
+ output << cipher.update(data)
56
+ end
57
+ end
58
+
59
+ output << cipher.final
60
+ end
61
+
62
+ # Encrypt a node and insert it into the local store
63
+ class Builder
64
+ def initialize
65
+ @hash_function = Cryptosphere.hash_function
66
+ @hash_function << PREFIX
67
+
68
+ @file = Tempfile.new 'cryptosphere'
69
+ end
70
+
71
+ def write(data)
72
+ @hash_function << data
73
+ @file << data
74
+ end
75
+ alias_method :<<, :write
76
+
77
+ def finish
78
+ keys = Cryptosphere.kdf(@hash_function.digest, size: 64)
79
+ key, iv = keys[0...32], keys[32...64]
80
+
81
+ block_cipher = Cryptosphere.block_cipher
82
+ block_cipher.encrypt
83
+ block_cipher.key, block_cipher.iv = key, iv
84
+
85
+ @file.rewind
86
+ output = Tempfile.new 'cryptosphere'
87
+
88
+ begin
89
+ hash_function = Cryptosphere.hash_function
90
+ while plaintext = @file.read(4096)
91
+ ciphertext = block_cipher.update(plaintext)
92
+ output << ciphertext
93
+ hash_function << ciphertext
94
+ end
95
+
96
+ ciphertext = block_cipher.final
97
+ output << ciphertext
98
+ hash_function << ciphertext
99
+ output.close
100
+
101
+ node_id = hash_function.hexdigest
102
+ FileUtils.mv output.path, File.join(Blob.path, node_id)
103
+
104
+ Blob.new(node_id, key + iv)
105
+ rescue Exception
106
+ output.close rescue nil
107
+ output.unlink rescue nil
108
+
109
+ raise
110
+ end
111
+ ensure
112
+ @file.close rescue nil
113
+ @file.unlink rescue nil
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,22 @@
1
+ module Cryptosphere
2
+ class Tree < Blob
3
+ def self.[](*entries)
4
+ new(entries)
5
+ end
6
+
7
+ def initialize(entries)
8
+ @entries = entries
9
+ end
10
+
11
+ def to_s
12
+ @entries.sort_by { |e| e.name }.map(&:to_s).join("\n")
13
+ end
14
+
15
+ # Individual entries in a tree
16
+ class Entry < Struct.new(:mode, :type, :id, :key, :name)
17
+ def to_s
18
+ "#{mode} #{type} #{id} #{key} #{name}"
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,11 @@
1
+ require 'thor'
2
+
3
+ module Cryptosphere
4
+ class CLI < Thor
5
+
6
+ desc 'setup', 'Configure the Cryptosphere'
7
+ def setup
8
+ puts "Hi there!"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,73 @@
1
+ require 'openssl'
2
+
3
+ module Cryptosphere
4
+ # Asymmetric encryption cipher: 2048-bit RSA
5
+ class AsymmetricCipher
6
+ KEY_SIZE = 2048
7
+
8
+ def self.generate_key
9
+ OpenSSL::PKey::RSA.generate(KEY_SIZE).to_pem
10
+ end
11
+
12
+ def initialize(key)
13
+ openssl_key = OpenSSL::PKey::RSA.new(key)
14
+
15
+ if openssl_key.private?
16
+ @private_key = openssl_key
17
+ @public_key = openssl_key.public_key
18
+ else
19
+ @private_key = nil
20
+ @public_key = openssl_key
21
+ end
22
+ end
23
+
24
+ # Serialize canonical private key with Distinguished Encoding Rules (DER)
25
+ def private_key
26
+ @private_key.to_der
27
+ end
28
+
29
+ # Serialize private key in Privacy Enhanced Mail (PEM) format
30
+ def private_key_pem
31
+ @private_key.to_pem
32
+ end
33
+
34
+ # Is a private key present?
35
+ def private_key?
36
+ !!@private_key
37
+ end
38
+
39
+ # Serialize canonical public key with Distinguished Encoding Rules (DER)
40
+ def public_key
41
+ @public_key.to_der
42
+ end
43
+
44
+ # Obtain the fingerprint for this public key
45
+ def public_key_fingerprint
46
+ Cryptosphere.kdf(public_key).unpack('H*').first.scan(/.{4}/).join(":")
47
+ end
48
+
49
+ # Encrypt a value using the private key
50
+ # Value can be decrypted with the public key
51
+ def private_encrypt(plaintext)
52
+ @private_key.private_encrypt(plaintext)
53
+ end
54
+
55
+ # Decrypt a value using the private key
56
+ # Ciphertext must be encrypted with public key
57
+ def private_decrypt(ciphertext)
58
+ @private_key.private_decrypt(ciphertext)
59
+ end
60
+
61
+ # Encrypt a value using the public key
62
+ # Value can only be decrypted with the private key
63
+ def public_encrypt(plaintext)
64
+ @public_key.public_encrypt(plaintext)
65
+ end
66
+
67
+ # Decrypt a value using the public key
68
+ # Ciphertext must be encrypted with private key
69
+ def public_decrypt(ciphertext)
70
+ @public_key.public_decrypt(ciphertext)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,12 @@
1
+ require 'hkdf'
2
+
3
+ module Cryptosphere
4
+ # Cryptographically secure key derivation function: HKDF (RFC 5869)
5
+ #
6
+ # Options:
7
+ # * size: how many bytes of output to generate (default 32, i.e. 256 bits)
8
+ def self.kdf(secret, options = {})
9
+ size = options[:size] || 32
10
+ HKDF.new(secret).next_bytes(size)
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ module Cryptosphere
2
+ # Sign the given message with a private key
3
+ def self.sign(key, message)
4
+ AsymmetricCipher.new(key).private_encrypt(kdf(message))
5
+ end
6
+
7
+ # Verify a message with the public key. Returns if the signature matches,
8
+ # and false if there's a mismatch
9
+ def self.verify(key, message, signature)
10
+ AsymmetricCipher.new(key).public_decrypt(signature) == kdf(message)
11
+ end
12
+
13
+ # Verify a message, raising InvalidSignatureError on signature mismatch
14
+ def self.verify!(key, message, signature)
15
+ verify(key, message, signature) or raise InvalidSignatureError, "signature mismatch"
16
+ end
17
+ end
@@ -0,0 +1,77 @@
1
+ module Cryptosphere
2
+ class Head
3
+ attr_reader :signing_key, :read_key, :verify_key, :timestamp
4
+
5
+ def self.generate
6
+ access_key = AsymmetricCipher.generate_key
7
+ read_key = Cryptosphere.random_bytes(32)
8
+
9
+ new(verify_key.to_der, read_key, signing_key.to_der)
10
+ end
11
+
12
+ def initialize(access_key, read_key = nil)
13
+ @signing_cipher = AsymmetricCipher.new(access_key)
14
+ @read_key = read_key
15
+
16
+ @id = @signing_cipher.public_key_fingerprint
17
+ @location = nil
18
+ @timestamp = nil
19
+ end
20
+
21
+ def location
22
+ raise CapabilityError, "can't read location" unless @read_key
23
+ @location
24
+ end
25
+
26
+ def move(location, timestamp = Time.now)
27
+ raise CapabilityError, "don't have write capability" unless @signing_cipher.private_key?
28
+ @location, @timestamp = location, timestamp
29
+ end
30
+ alias_method :location=, :move
31
+
32
+ def to_signed_message
33
+ cipher = Cryptosphere.block_cipher
34
+ cipher.encrypt
35
+ cipher.key = @read_key
36
+ cipher.iv = iv = cipher.random_iv
37
+
38
+ ciphertext = cipher.update(location)
39
+ ciphertext << cipher.final
40
+
41
+ message = [@timestamp.to_i, iv, ciphertext].pack("QA16A*")
42
+ signature = @signing_cipher.private_encrypt Cryptosphere.kdf(message)
43
+
44
+ [signature.size, signature, message].pack("nA*A*")
45
+ end
46
+
47
+ def update(signed_message)
48
+ signature_size, rest = signed_message.unpack("nA*")
49
+ signature, message = rest.unpack("A#{signature_size}A*")
50
+
51
+ if @signing_cipher.public_decrypt(signature) != Cryptosphere.kdf(message)
52
+ raise InvalidSignatureError, "signature does not match message"
53
+ end
54
+
55
+ timestamp, iv, ciphertext = message.unpack("QA16A*")
56
+ timestamp = Time.at(timestamp)
57
+
58
+ if timestamp > Time.now
59
+ raise InvalidTimestampError, "timestamp is in the future"
60
+ elsif @timestamp && timestamp < @timestamp
61
+ return false # we have a newer version
62
+ end
63
+
64
+ if @read_key
65
+ cipher = Cryptosphere.block_cipher
66
+ cipher.decrypt
67
+ cipher.key = @read_key
68
+ cipher.iv = iv
69
+
70
+ location = cipher.update(ciphertext)
71
+ location << cipher.final
72
+
73
+ @location = location
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,21 @@
1
+ module Cryptosphere
2
+ class Identity
3
+ attr_reader :id
4
+
5
+ extend Forwardable
6
+ def_delegators :@cipher, :private_key, :public_key
7
+
8
+ def self.generate
9
+ new AsymmetricCipher.generate_key
10
+ end
11
+
12
+ def initialize(private_key)
13
+ @cipher = AsymmetricCipher.new(private_key)
14
+ @id = @cipher.public_key_fingerprint
15
+ end
16
+
17
+ def to_s
18
+ "#<Cryptosphere::Identity:#{id}>"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,37 @@
1
+ module Cryptosphere
2
+ module Handshake
3
+ module_function
4
+
5
+ def encode_request(sender, recipient)
6
+ # TODO: encrypt sender's public key
7
+ # Sure would be nice to have some Curve25519 here
8
+ message = sender.public_key
9
+ Cryptosphere.sign(sender.private_key, message) + message
10
+ end
11
+
12
+ def decode_request(recipient, message)
13
+ bytes = PUBKEY_SIZE / 8
14
+ signature, message = message[0...bytes], message[bytes..-1]
15
+
16
+ # FIXME: this should be encrypted :(
17
+ sender_key = message
18
+ Cryptosphere.verify!(sender_key, message, signature)
19
+
20
+ sender_key
21
+ end
22
+
23
+ def encode_response(sender, recipient, secret = Cryptosphere.random_bytes(32))
24
+ cipher = AsymmetricCipher.new(recipient.public_key)
25
+ message = cipher.public_encrypt(secret)
26
+ Cryptosphere.sign(sender.private_key, message) + message
27
+ end
28
+
29
+ def decode_response(recipient, sender, message)
30
+ bytes = PUBKEY_SIZE / 8
31
+ signature, message = message[0...bytes], message[bytes..-1]
32
+ Cryptosphere.verify!(sender.public_key, message, signature)
33
+ cipher = AsymmetricCipher.new(recipient.private_key)
34
+ cipher.private_decrypt(message)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,3 @@
1
+ module Cryptosphere
2
+ VERSION = "0.0.0"
3
+ end
Binary file
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cryptosphere::Blob do
4
+ it "creates blobs from data" do
5
+ builder = Cryptosphere::Blob::Builder.new
6
+ builder << "foobar"
7
+ blob = builder.finish
8
+ blob.decrypt.should == "foobar"
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cryptosphere::Tree do
4
+ it "creates nodes" do
5
+ file = Cryptosphere::Blob["file::foobar"]
6
+ foobar = Cryptosphere::Tree::Entry['-', 'file', file.id, file.key, "foobar"]
7
+ tree = Cryptosphere::Tree[foobar]
8
+ tree.to_s.should == "- file #{file.id} #{file.key} foobar"
9
+ end
10
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cryptosphere::Head do
4
+ let(:read_key) { "X" * 32 }
5
+ let(:write_key) { example_private_key }
6
+ let(:verify_key) { example_public_key }
7
+
8
+ let(:reader) { Cryptosphere::Head.new(verify_key, read_key) }
9
+ let(:writer) { Cryptosphere::Head.new(write_key, read_key) }
10
+
11
+ let(:example_location) { "221B Baker Street" }
12
+
13
+ it "moves heads" do
14
+ writer.move(example_location)
15
+ message = writer.to_signed_message
16
+ message.length.should be < MAX_DATAGRAM_LENGTH
17
+
18
+ reader.update message
19
+ reader.location.should == example_location
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cryptosphere::Identity do
4
+ it "generates identities" do
5
+ Cryptosphere::Identity.generate.should be_a Cryptosphere::Identity
6
+ end
7
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Cryptosphere::Handshake do
4
+ let(:alice) { Cryptosphere::Identity.new(alice_private_key) }
5
+ let(:bob) { Cryptosphere::Identity.new(bob_private_key) }
6
+ let(:secret) { "X" * 32 }
7
+
8
+ it "encodes and decodes request packets" do
9
+ packet = Cryptosphere::Handshake.encode_request(alice, bob)
10
+
11
+ # FIXME: need elliptic curve crypto here for smaller packets
12
+ #packet.length.should be < MAX_DATAGRAM_LENGTH
13
+ public_key = Cryptosphere::Handshake.decode_request(bob, packet)
14
+ public_key.should eq alice.public_key
15
+ end
16
+
17
+ it "encodes and decodes response packets" do
18
+ packet = Cryptosphere::Handshake.encode_response(bob, alice, secret)
19
+ response = Cryptosphere::Handshake.decode_response(alice, bob, packet)
20
+ response.should eq secret
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEpQIBAAKCAQEAzwQYjchBgWq7nStqQeTvRhC15kGJc2Aa3q3ytIt7iH3gQSH+
3
+ KbZKckGh184JnPoCEOUDHJ2klhpTkjf0kgAcx6TOUt9DdP63B3FpX22LG0PU4qbm
4
+ SxZ4QXsqhEhj2UyxMbkKbBE9lKx90K9SO7dDB797cghoD5Sh2T69y+4bg6etJpVv
5
+ x0oP+9/HUynQBwZqykHIXPyhcBGsaWcB52tmwwYVQCkhaOhQv3iI4Z+33M2gAbpr
6
+ cH83kJCnKVZlRnhrswsxUUQ5nrX66edNXztcz28Lj6r8bMrQlymd+1zFjVCX1h5e
7
+ h2n/DNVRU5LkeK3VPom7mQ/xZygdhvEksebsSwIDAQABAoIBACDyJ+44lqRAFke3
8
+ JxwBkUr3UdupRnTEMMKLsHqnUCuyzMPQ4yBEUKjKZTVxJvqCl12U9N/S/uScn/w/
9
+ R38M4YesZOGvgo7WEs7ub7SuPFtEelbv9OqyUsUpEuUmmC13FSQyrMPyInjM0uEp
10
+ Zc73JYXQJZdKWzVPlEp8v7v60woqlchFzFk9i+byhN40gKUx4Irm6UCeMRBlmPP8
11
+ 6Ck3iUZw0I6NARF9tRNa0gn+nDoX7bBJfohsb5yLYvDHjv9ED2u4ivDyk5X0HvPP
12
+ nHCBFgtt5MK0WVdaaIKxXXPJaPCXVlpYTCLmwqjuVQslPZXzv0CAhO40ZvMnLfAn
13
+ xZ9MCjECgYEA+ysg7gE5WoWL/hPVS0JA5tKqn83f4tinWAnRgbPg7cUnFVrNI3qZ
14
+ riPvj/SdFQMrGEsnq6RH7hoBfKPIEImWETaNMgWvGEegs1JDzHXI40BUZBmm1ioZ
15
+ tCF0hmDibYckJBhU8BKSiM0wAx3QEbC/VzKsSto2mOICIidEyw+Wk6cCgYEA0v+K
16
+ MGhOusBxDDx/mRlW3/1+nCzyla+NDTRKcJaDGOArgU+AOqRcHuyiBdD25fyhs3JC
17
+ 3GsxJmOvO+UrX2tFpNycEpmMR/XYPVWZlNbaoOp15/+ijx9OAOTMmjOQ26HUHhar
18
+ 3jP5DLbodcnnuHCeTNpW8zTswbqnArRPYXZKBr0CgYEAmvJaWDmtFij42f+GP+1Z
19
+ eIxR8k/hZGJfqjI0ax17D3Pmzoe7sb16fTFyIo63MTVJKq2ChaLNNRgZ/rhTPdCD
20
+ IY9Tv54+DG5ztuxzIvkuuvL+nNouUEScosFYz2WJiiQqqZHRJGFwwLBEhEeqCp/N
21
+ CpAaNfs0X1BeHI+5IsQ1ElUCgYEAmuULnlPEkCZcFx6GkW7frtmaS65Xe3l/c9US
22
+ XKqxnN5cMbaaLPKhyfXvT5PC3L1kO6bC3Ks4TrVZW//1ojvOyaNGVAUyzVT2JLil
23
+ YXWE1CKq4eBxht31VoSgiwcV7ZZUcK42B45h42qXJnlNScIrA8I5mJsev211029o
24
+ 4uSCnYUCgYEAlpfHrPIvHzjrgg2+d18hFmF1139zeAfuDVNpvRC2sjTZVAWfmyDj
25
+ 9rlsBXPM8vg2AUojiKy4Q+CuhIcBKOpMAEgmU33sH6/rEZTPmQrztx4f1YJHHQjR
26
+ JyL+dplE4InNOiO2m8ymQA7JfRa4hC+VZnVUNxPl1TcB2yp/iMZyg8M=
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,27 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIIEowIBAAKCAQEAycdXNUdBLGbnyKt8M8LhT9sObVzvKY+WQJsdvxyPoROaS8aY
3
+ ZxFTLHxSbJpHp9Yl+A3ZZjoNdE0b6mc771S/PorY6SQc+KOGbDP7864fn5y+haYP
4
+ BT8qBB93wZdkCIQay5HnAS1xdIzAFCAsu3dKVgfxQt/+sFnSw/qtC1BuxCqXckDk
5
+ HVYcgkdQUXKh1prj240M/NfGjYIPfHnGvAKwhTKzJYVPSKDtgs432NcD5u8Oa+eU
6
+ yrA8PpaeXdwnndbjXbyvXwWc1sep77NHXkcZNpOqC9vKY+PHXbHvpV+pILeZxH7z
7
+ lE9mf/l0ucbmCH4EnLx+ZXTU2lZMTCqH2JwaRwIDAQABAoIBAF85A57REaCyr4+z
8
+ 3dlPjqTw684QnY0vhejXSyJ1iBKr/ZTlE+cP9gB4ay11YXuDREfbwUzM+Kx590KX
9
+ lWFMzTPmspbTxBhSk41cuvo0ohfhEMhhpZUESf/IGevyVfLu5PZM1IdpurEV+0+E
10
+ H5gYo6wV83Vr3/W5bg2urxs3yg4odNH59f5RIi++q3YpIXctZErboh6nEGNoP4aK
11
+ NsCmPv+dTheryHbY29OBCqtFZWNma57gNViuuliDQYlgqbjvOGE2jEm6PmqOYuN/
12
+ /nRlgD0hFscJUGfnxjm7b2pu3J9hLosiJ7WCkKuA6QkFYBX8IbiCGPX0QB/uVS2P
13
+ bNgJaYECgYEA6mTjPZAE6NG1lErp5FRknuzm4R1Damm0SVYC3K6kkIimXBf4z0lk
14
+ TB/l0j5e7wS7b29eoQcylXoxLAS/tG3odEDyZXwncncWjYyvwOeonfxRCFxhKyEP
15
+ GV6ATz/arOUqjN7b39j6grtrNy+SLg6xpGwnue3wVUz4m4TENRtlSvMCgYEA3GDP
16
+ nNDNBqdsjzjSccCZXo6AL+TNDP0PkhzZNGRFXLn2kr2bmL2rdS7A8iYwbYsX0US/
17
+ WUTZAfTBaC2nWa4RyKLTLrwChKflccrz4a0EjTeZvheSy+HRyBMLrIg7bK+VaTxg
18
+ kgNKxfxMYb7L4sqMvgyG2XUoI70wOPUvRqAooF0CgYBOn162BLwQ2F8fCe3goApM
19
+ YMylECrP4/sMamR1X8Nlk+CxnXzhEw4olr6BQliXti+lFmdYflCSYTVjPYiMXh2N
20
+ +UcVkYqt05JmEp3ViB9ANyV7N3mEfQdSjCdf7dxNCGW4cPyx3ldRSMqS7UsMVfNy
21
+ YrEEpfJqunHdwLAL3E+izwKBgHLH4ZpVjlPG4938xG8G11rOcamAS+RV8cQyTlbh
22
+ Wtce7HQlWWNGdoUEIu58QYDsjy0p11fRag2AwzNVg/JLnWuYktGmjtE0+WY5RKjo
23
+ CpTavrKpaIXUSgUhFlV4ZysGkJVNIycpm8pezBGk5GBtCrz5nUqpoIxsvy8LVOI+
24
+ DKrpAoGBAL3g75JCxSuCxETK+bDns6h/t4YDmyxWsZOp+SZE4j3pYbILwQQr24l8
25
+ FVNsGkLiti5k2pgHB6jDAPEiZBzR9zXAoBxJaM5ga1XirdC4YkFVULP+vUflHbRz
26
+ jz8R+pzjVuUr+b/sTeudgh8FRofHcHi0fNVK7XnLgmQaj6mTArf5
27
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,43 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+ require 'cryptosphere'
4
+ require 'fileutils'
5
+
6
+ Root = File.expand_path("../tmp", __FILE__)
7
+
8
+ FileUtils.rm_rf File.join(Root, "*")
9
+ Cryptosphere::Blob.setup :root => Root
10
+
11
+ module KeyExamples
12
+ def load_fixture_private_key(name = 'alice.key')
13
+ File.read File.expand_path("../fixtures/#{name}", __FILE__)
14
+ end
15
+
16
+ def load_fixture_public_key(name = 'alice.key')
17
+ Cryptosphere::AsymmetricCipher.new(load_fixture_private_key(name)).public_key
18
+ end
19
+
20
+ def alice_private_key
21
+ @alice_private_key ||= load_fixture_private_key('alice.key')
22
+ end
23
+ alias_method :example_private_key, :alice_private_key
24
+
25
+ def alice_public_key
26
+ @alice_public_key ||= load_fixture_public_key('alice.key')
27
+ end
28
+ alias_method :example_public_key, :alice_public_key
29
+
30
+ def bob_private_key
31
+ @bob_private_key ||= load_fixture_private_key('bob.key')
32
+ end
33
+
34
+ def bob_public_key
35
+ @bob_public_key ||= load_fixture_public_key('bob.key')
36
+ end
37
+ end
38
+
39
+ include KeyExamples
40
+
41
+ # Maximum datagram length the Cryptosphere protocol permits
42
+ # Taken from RFC 879 maximum size reassembly buffer
43
+ MAX_DATAGRAM_LENGTH = 512
@@ -0,0 +1,7 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new
4
+
5
+ RSpec::Core::RakeTask.new(:rcov) do |task|
6
+ task.rcov = true
7
+ end
metadata ADDED
@@ -0,0 +1,143 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cryptosphere
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tony Arcieri
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-08-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: celluloid
16
+ requirement: &70140313417880 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70140313417880
25
+ - !ruby/object:Gem::Dependency
26
+ name: thor
27
+ requirement: &70140313417460 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70140313417460
36
+ - !ruby/object:Gem::Dependency
37
+ name: hkdf
38
+ requirement: &70140313417040 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70140313417040
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &70140313416620 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70140313416620
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: &70140313416200 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70140313416200
69
+ description: A decentralized globally distributed peer-to-peer data archive
70
+ email:
71
+ - tony.arcieri@gmail.com
72
+ executables:
73
+ - csphere
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - .gitignore
78
+ - .rspec
79
+ - .travis.yml
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - bin/csphere
85
+ - cryptosphere.gemspec
86
+ - lib/cryptosphere.rb
87
+ - lib/cryptosphere/blobs/blob.rb
88
+ - lib/cryptosphere/blobs/tree.rb
89
+ - lib/cryptosphere/cli.rb
90
+ - lib/cryptosphere/crypto/asymmetric_cipher.rb
91
+ - lib/cryptosphere/crypto/kdf.rb
92
+ - lib/cryptosphere/crypto/signature_algorithm.rb
93
+ - lib/cryptosphere/head.rb
94
+ - lib/cryptosphere/identity.rb
95
+ - lib/cryptosphere/protocol/handshake.rb
96
+ - lib/cryptosphere/version.rb
97
+ - logo.png
98
+ - spec/cryptosphere/blobs/blob_spec.rb
99
+ - spec/cryptosphere/blobs/tree_spec.rb
100
+ - spec/cryptosphere/head_spec.rb
101
+ - spec/cryptosphere/identity_spec.rb
102
+ - spec/cryptosphere/protocol/handshake_spec.rb
103
+ - spec/fixtures/alice.key
104
+ - spec/fixtures/bob.key
105
+ - spec/spec_helper.rb
106
+ - spec/tmp/.gitkeep
107
+ - tasks/rspec.task
108
+ homepage: http://github.com/tarcieri/cryptosphere
109
+ licenses: []
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ none: false
116
+ requirements:
117
+ - - ! '>='
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ requirements: []
127
+ rubyforge_project:
128
+ rubygems_version: 1.8.10
129
+ signing_key:
130
+ specification_version: 3
131
+ summary: The Cryptosphere is a P2P cryptosystem for publishing and securely distributing
132
+ content anonymously with no central point of failure
133
+ test_files:
134
+ - spec/cryptosphere/blobs/blob_spec.rb
135
+ - spec/cryptosphere/blobs/tree_spec.rb
136
+ - spec/cryptosphere/head_spec.rb
137
+ - spec/cryptosphere/identity_spec.rb
138
+ - spec/cryptosphere/protocol/handshake_spec.rb
139
+ - spec/fixtures/alice.key
140
+ - spec/fixtures/bob.key
141
+ - spec/spec_helper.rb
142
+ - spec/tmp/.gitkeep
143
+ has_rdoc: