keybase-core 0.1.2

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.
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