# https://blog.spideroak.com/20140814060007-status-reports-transparency-overall-safety | |
# https://spideroak.com/canary | |
# https://en.wikipedia.org/wiki/Warrant_canary | |
# https://www.eff.org/deeplinks/2014/04/warrant-canary-faq | |
# https://en.wikipedia.org/wiki/National_security_letter | |
# | |
# SpiderOak now maintains a warrant canary so they can passively let their users know | |
# if they have been served a National Security Letter or other legal tool which | |
# prevents them from actively disclosing to their users that they are being coerced | |
# or forced into compromising the security or privacy of their userbase. | |
# | |
# If the canary has not been killed and you can still trust their service then you will | |
# be able to verify 3 valid PGP signatures against their canary. | |
# | |
# The signatures will be made every six months between: | |
# August 10 - August 15 | |
# February 10 - February 15 | |
# | |
# The default signing keys are listed below however they may be replaced or updated | |
# if one of the default signers goes rogue or leaves SpiderOak. | |
# | |
# Key ID: 0x1712D3E36F2F0403 | |
# Primary key fingerprint: 0DAB 1518 36A3 CBBA 0362 FC17 1712 D3E3 6F2F 0403 | |
# | |
# Key ID: 0xE435F15CA6B145F8 | |
# Primary key fingerprint: B411 3438 B56B D51C D208 E17E E435 F15C A6B1 45F8 | |
# | |
# Key ID: 0x132B9F2251131E5C | |
# Primary key fingerprint: DC1E FB15 4444 E4B5 2726 8430 132B 9F22 5113 1E5C | |
# | |
# Example output: | |
# | |
# Chip Black (SpiderOak Canary) <chip@spideroak.com> signed canary on August 11 2014 with key: 0DAB 1518 36A3 CBBA 0362 FC17 1712 D3E3 6F2F 0403 | |
# Frank Sievertsen <frank@spideroak.com> signed canary on August 12 2014 with key: B411 3438 B56B D51C D208 E17E E435 F15C A6B1 45F8 | |
# Tomas Touceda (SpiderOak canary) <tomas@spideroak.com> signed canary on August 13 2014 with key: DC1E FB15 4444 E4B5 2726 8430 132B 9F22 5113 1E5C | |
# SpiderOak's Canary is alive and well! | |
# | |
# NOTE: This needs to have error handling added in various places, communicating with the network | |
# running GPG commands, handling if you actually do trust the signing keys, and the date ranges | |
# for the signature checking needs to be made more generic and the code should be refactored | |
# but its a start. | |
require 'open3' | |
require 'open-uri' | |
require 'tempfile' | |
class SpiderOakCanary | |
CANARY_URL = 'https://spideroak.com/canary' | |
DELIMITER = "-----BEGIN PGP SIGNATURE-----\n" | |
DEFAULT_KEYS = [ | |
'0DAB 1518 36A3 CBBA 0362 FC17 1712 D3E3 6F2F 0403', | |
'B411 3438 B56B D51C D208 E17E E435 F15C A6B1 45F8', | |
'DC1E FB15 4444 E4B5 2726 8430 132B 9F22 5113 1E5C' | |
] | |
def verify(out_stream) | |
lines = open(CANARY_URL).readlines | |
chunk_idx = 0 | |
chunks = lines.chunk { |line| | |
DELIMITER == line ? chunk_idx+=1 : chunk_idx | |
}.map { |chunk| | |
chunk.last.join | |
} | |
canary, signatures = chunks.shift, chunks | |
expected_signatures = {} | |
canary_file = Tempfile.new('') | |
canary_file.write(canary) | |
canary_file.close | |
signatures.each do |signature| | |
command = "gpg --verify - #{canary_file.to_path}" | |
stdin, stdout, stderr = Open3.popen3(command) | |
stdin.puts signature | |
stdin.close | |
output = stderr.readlines | |
signature_date = DateTime.parse(output[0][/made (.*) using/, 1]) | |
signer = output[1][/Good signature from \"(.*)\"/, 1] | |
primary_key = output.select { |line| line.include?('Primary key') }.first | |
key_fingerprint = primary_key[/fingerprint: (.*)/, 1] | |
expected_signatures[key_fingerprint] = signature_date | |
out_stream.puts "#{signer} signed canary on #{signature_date.strftime('%B %d %Y')} " + | |
"with key: #{key_fingerprint}" | |
end | |
valid_signatures = DEFAULT_KEYS.select do |key| | |
signature_date = expected_signatures[key] | |
# | |
# TODO: Automatically determine the appropriate date range of signatures | |
# to check based on the current date or a passed in date. | |
# Remove hardcoded date range below. | |
# | |
#current_month = Date.today.month | |
#current_year = Date.today.year | |
#start_date = DateTime.parse("10-#{current_month}-#{current_year}") | |
#start_date = DateTime.parse("16-#{current_month}-#{current_year}") | |
start_date = DateTime.parse("August 10, 2014") | |
end_date = DateTime.parse("August 16, 2014") | |
(start_date...end_date).cover? signature_date | |
end.count | |
if valid_signatures == 3 | |
out_stream.puts "SpiderOak's Canary is alive and well!" | |
else | |
out_stream.puts "Hrmmm SpiderOak's Canary seems to have died, consider your options." | |
end | |
canary_file.unlink | |
end | |
end | |
# Run program if this file was executed, | |
# but not if file was just required | |
if $0 == __FILE__ | |
SpiderOakCanary.new.verify(STDOUT) | |
end |
require 'stringio' | |
require 'vcr' | |
require_relative 'canary' | |
VCR.configure do |c| | |
c.cassette_library_dir = '.' | |
c.hook_into :webmock | |
end | |
describe SpiderOakCanary, '#verify' do | |
let(:output) { <<-OUTPUT } | |
Chip Black (SpiderOak Canary) <chip@spideroak.com> signed canary on August 11 2014 with key: 0DAB 1518 36A3 CBBA 0362 FC17 1712 D3E3 6F2F 0403 | |
Frank Sievertsen <frank@spideroak.com> signed canary on August 12 2014 with key: B411 3438 B56B D51C D208 E17E E435 F15C A6B1 45F8 | |
Tomas Touceda (SpiderOak canary) <tomas@spideroak.com> signed canary on August 13 2014 with key: DC1E FB15 4444 E4B5 2726 8430 132B 9F22 5113 1E5C | |
SpiderOak's Canary is alive and well! | |
OUTPUT | |
it 'writes output' do | |
strio = StringIO.new | |
VCR.use_cassette('spideroak') { described_class.new.verify(strio) } | |
expect(strio.string).to eq output | |
end | |
end |
--- | |
http_interactions: | |
- request: | |
method: get | |
uri: https://spideroak.com/canary | |
body: | |
encoding: US-ASCII | |
string: '' | |
headers: | |
Accept-Encoding: | |
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3 | |
Accept: | |
- "*/*" | |
User-Agent: | |
- Ruby | |
response: | |
status: | |
code: 200 | |
message: OK | |
headers: | |
Server: | |
- nginx | |
Date: | |
- Wed, 10 Sep 2014 06:37:46 GMT | |
Content-Type: | |
- text/plain; charset=utf8 | |
Content-Length: | |
- '2376' | |
Last-Modified: | |
- Sun, 07 Sep 2014 19:58:37 GMT | |
Connection: | |
- keep-alive | |
Expires: | |
- Wed, 24 Sep 2014 06:37:46 GMT | |
Cache-Control: | |
- max-age=1209600 | |
Set-Cookie: | |
- uid=AAAAKlQP8bqZBDEMEIazAg==; expires=Thu, 10-Sep-15 06:37:46 GMT; path=/ | |
Accept-Ranges: | |
- bytes | |
body: | |
encoding: ASCII-8BIT | |
string: !binary |- | |
ZW46IEV2ZXJ5dGhpbmcncyBnb2luZyBzbW9vdGhseSBzbyBmYXIuCmRlOiBC | |
aXNoZXIgbMOkdWZ0IGFsbGVzIHByb2JsZW1sb3MuCmVzOiBUb2RvIHZhIGJp | |
ZW4gcG9yIGFob3JhLgoKSGF3YWlpYW4gR292ZXJub3IgTG9zZXMgUHJpbWFy | |
eSBieSBXaWRlIE1hcmdpbjsgU2VuYXRlIFJhY2UgSXMgVW5kZWNpZGVkIC0g | |
QXVnLiAxMCwgMjAxNCAtIFRoZSBOZXcgWW9yayB0aW1lcwoKYXVndXN0IDEw | |
LCAyMDE0Ci0tLS0tQkVHSU4gUEdQIFNJR05BVFVSRS0tLS0tClZlcnNpb246 | |
IEdudVBHIHYxLjQuMTMgKE9wZW5CU0QpCgppUUdjQkFBQkNnQUdCUUpUNk9m | |
ckFBb0pFQmNTMCtOdkx3UUR3RmtNQUk3MkFMWGswc3pxaUVhUlpiRGZIQ2l0 | |
CkZ4V05xQzlpMHk1akQ3bnNGUmtHOEVZaDJVdW85bEZrMWRRbG5qeFlqTGlu | |
c3R2NHorcDU3c1JmUHllWlpHNHIKZTZxZ3dwcUxyZzQvMDVlcndseVY4a2Vw | |
ZHAwdm9MdzlRbk9kbHl2REd5TmgrNGIxQ3duNU1pa044R1lseE5jVQpjV1J6 | |
VzI0SEJ0R2xHUlVhUVMxSUh6WEJhbjhHUU5GK2tNa1YzbnU0eVFoMXZxbFgw | |
K1NyeGtlUGJnbjJsOXVzClFmbGYxdnQza1JhRTBDRnZDZjU2Z2NWWTRRUzRL | |
ZERGVzFldllnbUdwMkRBblRXc2dLeC9pYU9STWt0ZkkxMEoKTzIvOGEvd2I4 | |
aVhsdUdOZjFZRmt0VXZtNllKblUybVJRRWRaSHZQTVphckFFTjRYTG5yK1dM | |
WGNWK0w3YzV3dgpNYjMvOFN6RXdIZ0RFRjhNTXludldZcmMyLzBFT3l4cU5i | |
L1M5a0JGMFhyNzdoRDdqU2RNMFVXZU51V2lKMlNlCndiUzJtWEJ3UDFjaTh3 | |
NDVxZlpNNXFNcWJDRlc4aExYcnMyb3dhYlRKZWc5OEp3em1QQ2pQblQ5dTJZ | |
RGttYTIKUEQ2RW0zemkzYlpiMHBnY1Nld2FEeFRUY1NDMlpCQlFBRnNXV3hR | |
R0NRPT0KPVRRQ3oKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0tLS0tCi0tLS0t | |
QkVHSU4gUEdQIFNJR05BVFVSRS0tLS0tClZlcnNpb246IEdudVBHIHYxCgpp | |
UUljQkFBQkNnQUdCUUpUNnBSQ0FBb0pFT1ExOFZ5bXNVWDRNZzRRQUk2VFFV | |
OGVUQW04VFRrKzhuOFFkVDUwCmZ6cXM2czNJaWp5SUU0UzR1Z1o4QjN2ZGIx | |
LysrR1VPL2ZXRGNZQXo0ZWFybGRUYTAxQWRxSTJuaUtDS0hqZEkKKzRMeTBs | |
dE9zZVN4cTJjeTFiWkl6Wlh3TEVuR3M4TXZObFBCd1pVeXlmYmFpeE16aGF0 | |
cTZ6cHZPSW9yVnRqYwpDeUlwQ0xwVEYxWDM2cUlQbmNwTURRY3owMFFGNFBP | |
N3VTRnJld3ZiYWRBaldaT3dncW80OW1yNW8yRlJKK1ZUCkpZV1IwbHNuVEor | |
OGhhbFJtTTZISFl6cWtVay8xTWVtOXJNN01NQ2FLVlBRNXYyOWIwNG8xL0Nv | |
dzE4TDd4MkwKODlQTzdPb2o2eXVyYkdFVERpQURSTElZc0xXTGxCZzFBVDZk | |
Z3Z4WHY5UnJQTXltRW1kRWRTbzJPUzByTjJPMgpKc2JEajArTHArcWdoc2Z2 | |
VlRBTVYrR3BsRXRWZzV1Q0grUFhoVG9mYmxLWEpERmtDK2dpR2hLTWNudlFD | |
aGM5Cm00c0wxUExxWTZzS3ozSUFwY0QwNnNBY0RNQzQzLzRZV1Axa081aEEz | |
bnBYcUFEZks0cnNxQnFuMWRSdUhxL0sKQTFVTzlqTEg2OVEwNHpyVUp3NlFS | |
WW0rWk1OK3BGTFpSREJiVFpUQm4yL3d4QkZCSkErVVBwbTVoSU1PalkvRwpi | |
dHFGYzI3cjZkWVZWOUYrZEVFOERNYnAvYjhLNUhySzJVbWZScDM1RUZaSUdT | |
ckk5dFlXSXhqQVIwVlk4eWVLCk9mTWFwRmsxaC9URlJ6cGpFTDNCcnNYUjNt | |
Slc1VGlndnQxV0xTTlQzMysvR1F0Qm9oRjBzQzRMdEgrZDBPUTEKZnJ6SzBk | |
L09xWWhmek1uZVVLeFcKPXUrMGcKLS0tLS1FTkQgUEdQIFNJR05BVFVSRS0t | |
LS0tCi0tLS0tQkVHSU4gUEdQIFNJR05BVFVSRS0tLS0tClZlcnNpb246IEdu | |
dVBHIHYxLjQuMTQgKERhcndpbikKCmlRR2NCQUFCQ2dBR0JRSlQ2MllTQUFv | |
SkVHUW10R1pxSnA4MXRwTUwvM3RkUXBCLzFWTVlGTTZDNFFLbC9RU0wKeXNs | |
ZWdLZXNhanc3S0dlMVFvS1B1enVobFp1QU1ETnYvSzQyZEVwNHR2N1BVbVlm | |
dFg4ZGpuYVN4bFQ4V1ZaSwp2ZWUvUmEycVV1UjBsbTZaUm94dDQwSDBiL2lO | |
ZGpwbHQzSEJSbUI4VUtMUTROWUZ4aStNazhBc3VGVFFyaGM3CktzTG11bzIz | |
anBONmU0dmVTTGwxWU1hWVI1TisvcEhTaDdCYVNSeVFEZitUdTZFSXR0ZExr | |
YzhuYlJ6RU9lbEMKQmtRWi9TQWN0YmR6elZyNG84TVVKaitZZzFZQ29tUlZF | |
TTVSUzc2WnNQZWRTUEJrWkUyMTRXYk9BMEZnTWx4NApGVkhxQkRTYndPUmdI | |
NW1EaFluaU43T1pxWXhta3pUVXdiaFA3eXVZekpUWi9GZUNGeG50TmRBSHdq | |
amFYWG54CjFLbm5NSmZ1Yk01WEtYbzJOTGxaZEFNRzVSYjVBcFV0b0hUQlN2 | |
Rjg3Z3BjZTFtS2l5aS9RSG1Zem5jcG11aXQKbElGVzdTVXViRnRrajRWOFR5 | |
NTlXNnB1TG5MbjhQZGFTSUk5RktXY3ZKMTU1UFBSTVNENG1SNlZ0QUJIQnAz | |
UgpZVWpPLy9paWFGMEJTOFErRXhUdUV5SmJLYlpUOGluWWI0b3hTeGxnM0E9 | |
PQo9L1gzdAotLS0tLUVORCBQR1AgU0lHTkFUVVJFLS0tLS0K | |
http_version: | |
recorded_at: Wed, 10 Sep 2014 06:37:46 GMT | |
recorded_with: VCR 2.9.2 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment