keybase-core 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +19 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +2 -0
  6. data/Gemfile.lock +49 -0
  7. data/LICENSE +23 -0
  8. data/README.md +137 -0
  9. data/Rakefile +20 -0
  10. data/keybase.gemspec +25 -0
  11. data/lib/keybase.rb +49 -0
  12. data/lib/keybase/crypto.rb +20 -0
  13. data/lib/keybase/error.rb +37 -0
  14. data/lib/keybase/models/dump.rb +33 -0
  15. data/lib/keybase/models/user.rb +168 -0
  16. data/lib/keybase/request/base.rb +32 -0
  17. data/lib/keybase/request/dump/all.rb +10 -0
  18. data/lib/keybase/request/dump/latest.rb +10 -0
  19. data/lib/keybase/request/key/add.rb +13 -0
  20. data/lib/keybase/request/key/revoke.rb +14 -0
  21. data/lib/keybase/request/root/get_salt_and_login_session.rb +12 -0
  22. data/lib/keybase/request/root/login.rb +14 -0
  23. data/lib/keybase/request/sig/post_auth.rb +13 -0
  24. data/lib/keybase/request/user/lookup.rb +10 -0
  25. data/lib/keybase/response.rb +14 -0
  26. data/lib/keybase/token_store.rb +32 -0
  27. data/test/all.rb +6 -0
  28. data/test/fixtures/example_dump.json +9 -0
  29. data/test/fixtures/example_user.json +50 -0
  30. data/test/integration/dumps_test.rb +23 -0
  31. data/test/integration/keys_test.rb +26 -0
  32. data/test/integration/login_users_test.rb +18 -0
  33. data/test/integration/lookup_users_test.rb +42 -0
  34. data/test/integration_test_helper.rb +6 -0
  35. data/test/test_helper.rb +17 -0
  36. data/test/unit/lib/keybase/crypto_test.rb +45 -0
  37. data/test/unit/lib/keybase/error_test.rb +32 -0
  38. data/test/unit/lib/keybase/models/dump_test.rb +31 -0
  39. data/test/unit/lib/keybase/models/user_test.rb +136 -0
  40. data/test/unit/lib/keybase/requests/base_test.rb +60 -0
  41. data/test/unit/lib/keybase/requests/dump/all_test.rb +17 -0
  42. data/test/unit/lib/keybase/requests/dump/latest_test.rb +17 -0
  43. data/test/unit/lib/keybase/requests/key/add_test.rb +19 -0
  44. data/test/unit/lib/keybase/requests/key/revoke_test.rb +19 -0
  45. data/test/unit/lib/keybase/requests/root/get_salt_test.rb +23 -0
  46. data/test/unit/lib/keybase/requests/root/login_test.rb +19 -0
  47. data/test/unit/lib/keybase/requests/sig/post_auth_test.rb +19 -0
  48. data/test/unit/lib/keybase/requests/user/lookup_test.rb +17 -0
  49. data/test/unit/lib/keybase/response_test.rb +23 -0
  50. data/test/unit/lib/keybase/token_store_test.rb +25 -0
  51. data/test/unit/lib/keybase_test.rb +25 -0
  52. data/test/vcr_cassettes/dumps.yml +49418 -0
  53. data/test/vcr_cassettes/keys.yml +349 -0
  54. data/test/vcr_cassettes/user_login.yml +176 -0
  55. data/test/vcr_cassettes/user_lookup_foo.yml +89 -0
  56. data/test/vcr_cassettes/user_lookup_invalid.yml +52 -0
  57. data/test/vcr_cassettes/user_lookup_missing.yml +52 -0
  58. data/test/vcr_cassettes/user_lookup_not_found.yml +55 -0
  59. metadata +200 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NjZmOGQ2MWU5MzM0NmYwNTVmZjEyMGM1YzM5YjkyNmU4ZTIxZjIzMQ==
5
+ data.tar.gz: !binary |-
6
+ MTE1YzllMWE1MTMyYTQzN2M3OTAyYTQ4N2QwNzhmMjcwYTE3ZWQ4Ng==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YTMwOTVjYTM3M2YyNzRlMTFkMzBhNmEwYTE1MTcxOWFjNTUyNDQxZjAxYzZj
10
+ MmU1NDNjYTc3MmI4ZWY0ZjY1ZTQ4MGIzOGJhNDZmZDI5YTMyMTkyNzhjODU5
11
+ MGUyNWJmMzMzNWM0NTM2YjdkMDYzYmQ3ZTAxODBmNTJmM2M4OWM=
12
+ data.tar.gz: !binary |-
13
+ N2UxOWU5NDQyMjUzZWQwZjZlMjJjN2I3MTI2YzZlMzMxODVhNTRjN2Q0NGRi
14
+ MzQxNDMxMmZkYTBhOGZlM2VjMjY4ZDk5YjgxMDFkMjJjNWVlOTU2ZmRjMjM5
15
+ Yzc4NTUyNzkwZjdhNDA0YWRlYmYxZmJiNmNhYzY0NDUwNDhiZTU=
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ coverage
6
+ InstalledFiles
7
+ lib/bundler/man
8
+ pkg
9
+ rdoc
10
+ spec/reports
11
+ test/tmp
12
+ test/version_tmp
13
+ tmp
14
+
15
+ # YARD artifacts
16
+ .yardoc
17
+ _yardoc
18
+ doc/
19
+ .DS_Store
@@ -0,0 +1 @@
1
+ 2.1.0
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.0
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
@@ -0,0 +1,49 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ keybase-core (0.1.1)
5
+ faraday (~> 0.9.0)
6
+ scrypt (~> 1.2.1)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ addressable (2.3.5)
12
+ crack (0.4.2)
13
+ safe_yaml (~> 1.0.0)
14
+ docile (1.1.3)
15
+ faraday (0.9.0)
16
+ multipart-post (>= 1.2, < 3)
17
+ ffi (1.9.3)
18
+ ffi-compiler (0.1.3)
19
+ ffi (>= 1.0.0)
20
+ rake
21
+ minitest (5.3.1)
22
+ multi_json (1.9.2)
23
+ multipart-post (2.0.0)
24
+ rake (10.1.1)
25
+ safe_yaml (1.0.1)
26
+ scrypt (1.2.1)
27
+ ffi-compiler (>= 0.0.2)
28
+ rake
29
+ simplecov (0.8.2)
30
+ docile (~> 1.1.0)
31
+ multi_json
32
+ simplecov-html (~> 0.8.0)
33
+ simplecov-html (0.8.0)
34
+ vcr (2.8.0)
35
+ webmock (1.8.11)
36
+ addressable (>= 2.2.7)
37
+ crack (>= 0.1.7)
38
+ yard (0.8.7.4)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ keybase-core!
45
+ minitest (~> 5.3.1)
46
+ simplecov (~> 0.8.2)
47
+ vcr (~> 2.8.0)
48
+ webmock (~> 1.8.0)
49
+ yard (~> 0.8.7.4)
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (c) 2014, keybase
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+
10
+ * Redistributions in binary form must reproduce the above copyright notice,
11
+ this list of conditions and the following disclaimer in the documentation
12
+ and/or other materials provided with the distribution.
13
+
14
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
18
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,137 @@
1
+ # Keybase Core Gem
2
+
3
+ API client gem for [keybase.io](https://keybase.io)
4
+
5
+ [![Build Status](https://secure.travis-ci.org/keybase/ruby-core.png?branch=master)](http://travis-ci.org/keybase/ruby-core) [![Gem Version](https://badge.fury.io/rb/keybase-core.png)](http://badge.fury.io/rb/keybase-core)
6
+
7
+ ## Installing
8
+
9
+ ```
10
+ gem install keybase-core
11
+ ```
12
+
13
+ or
14
+
15
+ (in Gemfile or .gemspec)
16
+ ```ruby
17
+ gem 'keybase-core'
18
+ ```
19
+
20
+ then
21
+
22
+ ```ruby
23
+ require 'keybase-core'
24
+ ```
25
+
26
+ ## Usage
27
+
28
+ Keybase is two things.
29
+
30
+ 1. A public, publicly-auditable directory of keys and identity proofs.
31
+ 2. A protocol for accessing the directory.
32
+
33
+ ### Login
34
+
35
+ Upon login, you'll get back a user object. Your user object is essentially a large dictionary (wrapped as a Ruby object) and contains pretty much everything about you and your account. (See the [user objects](https://keybase.io/__/api-docs/1.0#user-objects) page in the API documentation for more info.)
36
+
37
+ The login will also initiate an ongoing session which allows the authenticated actions: `key/add`, `key/revoke`, `sig/post_auth`
38
+
39
+ ```ruby
40
+ me = Keybase.login('chris', 'passphrase')
41
+ me.basics.username #=> "chris"
42
+ me.private_keys.primary.kid #=> "a140c70404a13370f7..."
43
+ ```
44
+
45
+ ### Key Add
46
+
47
+ An uploaded public key should be an armored PGP key. An uploaded private key should be in [P3SKB](https://keybase.io/__/api-docs/1.0#p3skb-format) format.
48
+
49
+ Note: You can't upload the private before the public.
50
+
51
+ ```ruby
52
+ me.add_public_key("-----BEGIN PGP PUBLIC...")
53
+ #=> "0101d9d962be6ee38cdadedd6..." (key_id)
54
+ me.add_private_key("hKRib2R5gqRwcml2gqRkY...")
55
+ #=> "a140c70404a13370f7..." (key_id)
56
+ ```
57
+
58
+ ### Key Revoke
59
+
60
+ Currently, the only acceptable type of revocation is a simple delete, which means you just delete the key from Keybase. It's technically not a revocation at all. In the near future, you will be able to post revocations to the server, too.
61
+
62
+ ```ruby
63
+ me.revoke_key("a140c70404a13370f7...") #=> true
64
+ ```
65
+
66
+ ### Signature Post Auth
67
+
68
+ Post a self-signed authentication certificate, so that future attempts to load a user session can succeed.
69
+
70
+ The payload of the signature should take the form of other keybase signatures, like self-signing keys, or proving ownership of remote accounts.
71
+
72
+ An example looks like:
73
+
74
+ ```json
75
+ {
76
+ "body": {
77
+ "key": {
78
+ "fingerprint": "da99a6ebeca98b14d944cb6e1ca9bfeab344f0fc",
79
+ "host": "keybase.io",
80
+ "key_id": "1ca9bfeab344f0fc",
81
+ "uid": "15a9e2826313eaf005291a1ae00c3f00",
82
+ "username": "taco422107"
83
+ },
84
+ "nonce": null,
85
+ "type": "auth",
86
+ "version": 1
87
+ },
88
+ "ctime": 1386537779,
89
+ "expire_in": 86400,
90
+ "tag": "signature"
91
+ }
92
+ ```
93
+
94
+ The client can provide an optional nonce to randomize the signatures. The server will check the signature for validatity, and on success, will return an auth_token, which is the SHA-256 hash of the full signature body, from the "---- BEGIN" through to the ---- END PGP MESSAGE ----.
95
+
96
+ ```ruby
97
+ me.post_auth('----- BEGIN PGP MESSAGE ----- ...')
98
+ #=> "fd2667b9b396150603ea0b567eaf3334c3..." (auth_token)
99
+ ```
100
+
101
+ ### Get User Information
102
+
103
+ A user object is a large dictionary (wrapped as a Ruby object) and contains pretty much everything about a user that you have access to.
104
+
105
+ ```ruby
106
+ user = Keybase.lookup('username')
107
+ user.basics.username
108
+ #=> "chris"
109
+ user.profile.bio
110
+ #=> "I am the terror that flaps in the night."
111
+ user.emails.primary
112
+ #=> "chris@okcupid.com"
113
+ user.emails.primary.is_verified?
114
+ #=> false
115
+ user.public_keys.primary.kid
116
+ #=> "d028ac1542b24a5f77f123ba467873e3ce96a992570a"
117
+ user.public_keys.primary.updated_at
118
+ #=> #<DateTime: 2013-11-05T21:00:12+00:00 ((2456602j,75612s,0n),+0s,2299161j)>
119
+ ```
120
+
121
+ ### Dumps
122
+
123
+ Dumps represent a history of the public dumps of the site. You can use it to track or mirror the site as you wish. If you have a previous dump of the site, you can apply changes to sync up without downloading the entire site.
124
+
125
+ ```ruby
126
+ dump = Keybase.dump_latest
127
+ dump.full_data_sha256
128
+ #=> "66e72ba9906e8eec544ae74ee22c249e6879ddcd856df0c9679d7a79f26ce259"
129
+ dump.changes_from_parent
130
+ #=> "https://s3.amazonaws.com/keybase_data_dumps/2014-03-24-18-06-15--1b43379e593576fe395ad90e--changes.json"
131
+
132
+ dumps = Keybase.dump_all #=> [#<Keybase::Dump:0x00000102584280...]
133
+ ```
134
+
135
+ ## Further Reading
136
+
137
+ Please check out the [Keybase API Documentation](https://keybase.io/__/api-docs/1.0) for a comprehensive explanation of the API and its capabilities.
@@ -0,0 +1,20 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ require 'rake/testtask'
5
+ namespace :test do
6
+ Rake::TestTask.new("unit") do |t|
7
+ t.libs << 'test'
8
+ t.pattern = "#{File.dirname(__FILE__)}/test/all.rb"
9
+ end
10
+
11
+ Rake::TestTask.new("integration") do |t|
12
+ t.libs << 'test'
13
+ t.pattern = "#{File.dirname(__FILE__)}/test/integration/*_test.rb"
14
+ end
15
+ end
16
+
17
+ desc "Run tests"
18
+ task :test => ["test:integration", "test:unit"]
19
+
20
+ task :default => :test
@@ -0,0 +1,25 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'keybase-core'
3
+ gem.version = '0.1.2'
4
+ gem.date = '2014-03-25'
5
+ gem.summary = 'keybase-core'
6
+ gem.description = 'Core Ruby lib for keybase.io'
7
+ gem.authors = [ 'Max Krohn', 'Sean Handley' ]
8
+ gem.email = 'themax@gmail.com'
9
+ gem.files = `git ls-files`.split("\n")
10
+ gem.homepage = 'https://github.com/keybase/ruby-core'
11
+ gem.license = 'BSD-3-Clause'
12
+
13
+ gem.required_ruby_version = '~> 2.1.0'
14
+
15
+ # Runtime Dependencies
16
+ gem.add_runtime_dependency 'scrypt', '~> 1.2.1'
17
+ gem.add_runtime_dependency 'faraday', '~> 0.9.0'
18
+
19
+ # Development Dependencies
20
+ gem.add_development_dependency 'vcr', '~> 2.8.0'
21
+ gem.add_development_dependency 'webmock', '~> 1.8.0'
22
+ gem.add_development_dependency 'minitest', '~> 5.3.1'
23
+ gem.add_development_dependency 'simplecov', '~> 0.8.2'
24
+ gem.add_development_dependency 'yard', '~> 0.8.7.4'
25
+ end
@@ -0,0 +1,49 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require 'date'
4
+ require 'scrypt'
5
+ require 'openssl'
6
+ require 'base64'
7
+ require 'securerandom'
8
+
9
+ module Keybase
10
+ require_relative 'keybase/crypto'
11
+ require_relative 'keybase/error'
12
+ require_relative 'keybase/models/user'
13
+ require_relative 'keybase/models/dump'
14
+ require_relative 'keybase/request/base'
15
+ require_relative 'keybase/request/dump/all'
16
+ require_relative 'keybase/request/dump/latest'
17
+ require_relative 'keybase/request/key/add'
18
+ require_relative 'keybase/request/key/revoke'
19
+ require_relative 'keybase/request/root/get_salt_and_login_session'
20
+ require_relative 'keybase/request/root/login'
21
+ require_relative 'keybase/request/sig/post_auth'
22
+ require_relative 'keybase/request/user/lookup'
23
+ require_relative 'keybase/response'
24
+ require_relative 'keybase/token_store'
25
+
26
+ # Lookup a user on Keybase
27
+ #
28
+ # @param [String] username the username of the user you are searching for
29
+ # @raise [Keybase::UserNotFoundError] if the user is not found
30
+ # @raise [Keybase::InputError] if the username is empty or invalid
31
+ # @return [Keybase::Model::User] the user, if they exist
32
+ def self.lookup(username)
33
+ User.lookup(username)
34
+ end
35
+
36
+ # Retrieve the dump history from Keybase
37
+ #
38
+ # @return [[Keybase::Dump]] a collection of all Keybase dumps
39
+ def self.dump_all
40
+ Dump.all
41
+ end
42
+
43
+ # Retrieve the latest dump from Keybase
44
+ #
45
+ # @return [Keybase::Dump] latest Keybase dump
46
+ def self.dump_latest
47
+ Dump.latest
48
+ end
49
+ end
@@ -0,0 +1,20 @@
1
+ module Keybase
2
+ # @private
3
+ class Crypto
4
+ SCRYPT_VARS = {'N' => 2**15, 'r' => 8, 'p' => 1, 'key_length' => 224, 'chunk' => 192..-1}
5
+ def self.scrypt(passphrase, salt)
6
+ SCrypt::Engine.scrypt(passphrase, hex2bin(salt),
7
+ SCRYPT_VARS['N'], SCRYPT_VARS['r'],
8
+ SCRYPT_VARS['p'], SCRYPT_VARS['key_length']
9
+ )[SCRYPT_VARS['chunk']]
10
+ end
11
+
12
+ def self.hmac_sha512(key, data)
13
+ OpenSSL::HMAC.hexdigest(OpenSSL::Digest::SHA512.new, key, Base64.decode64(data))
14
+ end
15
+
16
+ def self.hex2bin(hex)
17
+ [hex].pack("H*")
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,37 @@
1
+ module Keybase
2
+ # @private
3
+ class Error < StandardError
4
+ def self.raise_unless_successful(status)
5
+ return if status['code'] == 0
6
+ message = status['fields'] ? error_with_fields(status) : status['desc']
7
+ err = Keybase::errors[status['code']]
8
+ if err
9
+ raise err, message
10
+ else
11
+ raise Keybase::Error, "Error #{status['code']}: #{message}"
12
+ end
13
+ end
14
+
15
+ def self.error_with_fields(status)
16
+ "#{status['desc']}. #{status['fields'].map{|k,v| "#{k.to_s}: #{v.to_s}"}.join(',')}"
17
+ end
18
+ end
19
+
20
+ class UserNotFoundError < StandardError; end
21
+ class InputError < StandardError; end
22
+ class BadPasswordError < StandardError; end
23
+ class BadSessionError < StandardError; end
24
+ class CSRFVerificationError < StandardError; end
25
+
26
+ private
27
+
28
+ def self.errors
29
+ {
30
+ 100 => InputError,
31
+ 202 => BadSessionError,
32
+ 205 => UserNotFoundError,
33
+ 204 => BadPasswordError,
34
+ 222 => CSRFVerificationError
35
+ }
36
+ end
37
+ end
@@ -0,0 +1,33 @@
1
+ module Keybase
2
+ # A dump object containing hashes, IDs and data links
3
+ # from which a mirror can be configured.
4
+ class Dump
5
+
6
+ attr_reader :id, :full_data, :full_data, :full_data_sha256, :parent_dump_id, :changes_from_parent,
7
+ :changes_sha256, :created_at
8
+
9
+ def initialize(params)
10
+ @id = params['dump_id']
11
+ @full_data = params['full_data']
12
+ @full_data_sha256 = params['full_data_sha256']
13
+ @parent_dump_id = params['parent_dump_id']
14
+ @changes_from_parent = params['changes_from_parent']
15
+ @changes_sha256 = params['changes_sha256']
16
+ @created_at = Time.at(params['ctime']).to_datetime
17
+ end
18
+
19
+ # Retrieve the dump history from Keybase
20
+ #
21
+ # @return [[Keybase::Dump]] a collection of all Keybase dumps
22
+ def self.all
23
+ Request::Dump.all.map{|dump| new(dump)}
24
+ end
25
+
26
+ # Retrieve the latest dump from Keybase
27
+ #
28
+ # @return [Keybase::Dump] latest Keybase dump
29
+ def self.latest
30
+ new(Request::Dump.latest)
31
+ end
32
+ end
33
+ end