cryptosphere 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: