bullion 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.images/logo.png +0 -0
- data/.rubocop.yml +32 -0
- data/.travis.yml +12 -4
- data/Dockerfile +55 -0
- data/Gemfile +4 -2
- data/Gemfile.lock +148 -0
- data/LICENSE.txt +1 -1
- data/README.md +48 -16
- data/Rakefile +88 -3
- data/bin/console +4 -3
- data/bullion.gemspec +38 -15
- data/config.ru +22 -0
- data/config/puma.rb +3 -0
- data/db/migrate/20210104000000_create_accounts.rb +14 -0
- data/db/migrate/20210104060422_create_certificates.rb +18 -0
- data/db/migrate/20210105060406_create_orders.rb +19 -0
- data/db/migrate/20210106052306_create_authorizations.rb +16 -0
- data/db/migrate/20210106055421_create_challenges.rb +18 -0
- data/db/migrate/20210106060335_create_nonces.rb +12 -0
- data/db/schema.rb +92 -0
- data/lib/bullion.rb +93 -2
- data/lib/bullion/acme/error.rb +72 -0
- data/lib/bullion/challenge_client.rb +59 -0
- data/lib/bullion/challenge_clients/dns.rb +49 -0
- data/lib/bullion/challenge_clients/http.rb +33 -0
- data/lib/bullion/helpers/acme.rb +202 -0
- data/lib/bullion/helpers/service.rb +17 -0
- data/lib/bullion/helpers/ssl.rb +214 -0
- data/lib/bullion/models.rb +8 -0
- data/lib/bullion/models/account.rb +33 -0
- data/lib/bullion/models/authorization.rb +31 -0
- data/lib/bullion/models/certificate.rb +37 -0
- data/lib/bullion/models/challenge.rb +37 -0
- data/lib/bullion/models/nonce.rb +22 -0
- data/lib/bullion/models/order.rb +39 -0
- data/lib/bullion/service.rb +26 -0
- data/lib/bullion/services/ca.rb +370 -0
- data/lib/bullion/services/ping.rb +36 -0
- data/lib/bullion/version.rb +7 -1
- data/scripts/docker-entrypoint.sh +9 -0
- metadata +302 -17
data/bin/console
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
|
-
require
|
4
|
-
require
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'bullion'
|
5
6
|
|
6
7
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
8
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +11,5 @@ require "bullion"
|
|
10
11
|
# require "pry"
|
11
12
|
# Pry.start
|
12
13
|
|
13
|
-
require
|
14
|
+
require 'irb'
|
14
15
|
IRB.start(__FILE__)
|
data/bullion.gemspec
CHANGED
@@ -1,28 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require "bullion/version"
|
3
|
+
require_relative 'lib/bullion/version'
|
5
4
|
|
6
5
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
6
|
+
spec.name = 'bullion'
|
8
7
|
spec.version = Bullion::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
8
|
+
spec.authors = ['Jonathan Gnagy']
|
9
|
+
spec.email = ['jonathan.gnagy@gmail.com']
|
11
10
|
|
12
|
-
spec.summary = 'Ruby ACME v2 Certificate
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
11
|
+
spec.summary = 'Ruby ACME v2 Certificate Authority'
|
12
|
+
spec.homepage = 'https://github.com/jgnagy/bullion'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
|
15
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
16
|
+
spec.metadata['source_code_uri'] = 'https://github.com/jgnagy/bullion'
|
15
17
|
|
16
18
|
# Specify which files should be added to the gem when it is released.
|
17
19
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
18
|
-
spec.files
|
20
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
19
21
|
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
22
|
end
|
21
|
-
spec.bindir =
|
23
|
+
spec.bindir = 'exe'
|
22
24
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
-
spec.require_paths = [
|
25
|
+
spec.require_paths = ['lib']
|
26
|
+
|
27
|
+
spec.required_ruby_version = '~> 2.6'
|
28
|
+
|
29
|
+
spec.add_runtime_dependency 'httparty', '~> 0.18'
|
30
|
+
spec.add_runtime_dependency 'json', '~> 2.5'
|
31
|
+
spec.add_runtime_dependency 'jwt', '~> 1.5'
|
32
|
+
spec.add_runtime_dependency 'mysql2', '~> 0.5'
|
33
|
+
spec.add_runtime_dependency 'openssl', '~> 2.2'
|
34
|
+
spec.add_runtime_dependency 'prometheus-client', '~> 2.1'
|
35
|
+
spec.add_runtime_dependency 'puma', '~> 3.12'
|
36
|
+
spec.add_runtime_dependency 'sinatra', '~> 2.1'
|
37
|
+
spec.add_runtime_dependency 'sinatra-activerecord', '~> 2.0'
|
38
|
+
spec.add_runtime_dependency 'sinatra-contrib', '~> 2.1'
|
39
|
+
spec.add_runtime_dependency 'sqlite3', '~> 1.4'
|
24
40
|
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
41
|
+
spec.add_development_dependency 'acme-client', '~> 2.0'
|
42
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
43
|
+
spec.add_development_dependency 'byebug', '~> 9'
|
44
|
+
spec.add_development_dependency 'rack-test', '~> 0.8'
|
45
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
46
|
+
spec.add_development_dependency 'rspec', '~> 3.10'
|
47
|
+
spec.add_development_dependency 'rubocop', '~> 0.93'
|
48
|
+
spec.add_development_dependency 'simplecov', '~> 0.20'
|
49
|
+
spec.add_development_dependency 'simplecov-cobertura', '~> 1.4'
|
50
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
28
51
|
end
|
data/config.ru
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# \ -s puma
|
4
|
+
|
5
|
+
require 'bullion'
|
6
|
+
Bullion.validate_config!
|
7
|
+
|
8
|
+
require 'prometheus/middleware/collector'
|
9
|
+
require 'prometheus/middleware/exporter'
|
10
|
+
|
11
|
+
use Rack::ShowExceptions
|
12
|
+
use Rack::Deflater
|
13
|
+
use Prometheus::Middleware::Collector
|
14
|
+
use Prometheus::Middleware::Exporter
|
15
|
+
|
16
|
+
# Prometheus metrics are on /metrics
|
17
|
+
mappings = {
|
18
|
+
'/ping' => Bullion::Services::Ping.new,
|
19
|
+
'/acme' => Bullion::Services::CA.new
|
20
|
+
}
|
21
|
+
|
22
|
+
run Rack::URLMap.new(mappings)
|
data/config/puma.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Creates the accounts table
|
4
|
+
class CreateAccounts < ActiveRecord::Migration[6.1]
|
5
|
+
def change
|
6
|
+
create_table :accounts do |t|
|
7
|
+
t.boolean :tos_agreed, null: false, default: true, index: true
|
8
|
+
t.text :public_key, null: false, index: { unique: true }
|
9
|
+
t.text :contacts, null: false
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Creates the certificates table
|
4
|
+
class CreateCertificates < ActiveRecord::Migration[6.1]
|
5
|
+
def change
|
6
|
+
create_table :certificates do |t|
|
7
|
+
t.string :subject, null: false, index: true
|
8
|
+
t.string :csr_fingerprint, null: false, index: true
|
9
|
+
t.text :data, null: false
|
10
|
+
t.text :alternate_names
|
11
|
+
t.string :requester
|
12
|
+
t.boolean :validated, null: false, default: false, index: true
|
13
|
+
t.integer :serial, null: false, index: { unique: true }
|
14
|
+
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Creates the orders table
|
4
|
+
class CreateOrders < ActiveRecord::Migration[6.1]
|
5
|
+
def change
|
6
|
+
create_table :orders do |t|
|
7
|
+
t.string :status, null: false, default: 'pending', index: true
|
8
|
+
t.timestamp :expires, null: false, index: true
|
9
|
+
t.text :identifiers, null: false
|
10
|
+
t.timestamp :not_before, null: false
|
11
|
+
t.timestamp :not_after, null: false
|
12
|
+
t.references :certificate, foreign_key: true
|
13
|
+
|
14
|
+
t.belongs_to :account
|
15
|
+
|
16
|
+
t.timestamps
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Creates the authorizations table
|
4
|
+
class CreateAuthorizations < ActiveRecord::Migration[6.1]
|
5
|
+
def change
|
6
|
+
create_table :authorizations do |t|
|
7
|
+
t.string :status, null: false, default: 'pending', index: true
|
8
|
+
t.timestamp :expires, null: false, index: true
|
9
|
+
t.text :identifier, null: false
|
10
|
+
|
11
|
+
t.belongs_to :order
|
12
|
+
|
13
|
+
t.timestamps
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Creates the challenges table
|
4
|
+
class CreateChallenges < ActiveRecord::Migration[6.1]
|
5
|
+
def change
|
6
|
+
create_table :challenges do |t|
|
7
|
+
t.string :acme_type, null: false, index: true
|
8
|
+
t.string :status, null: false, default: :pending, index: true
|
9
|
+
t.timestamp :expires, null: false, index: true
|
10
|
+
t.string :token, null: false
|
11
|
+
t.timestamp :validated
|
12
|
+
|
13
|
+
t.belongs_to :authorization
|
14
|
+
|
15
|
+
t.timestamps
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/db/schema.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# This file is auto-generated from the current state of the database. Instead
|
2
|
+
# of editing this file, please use the migrations feature of Active Record to
|
3
|
+
# incrementally modify your database, and then regenerate this schema definition.
|
4
|
+
#
|
5
|
+
# This file is the source Rails uses to define your schema when running `bin/rails
|
6
|
+
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
|
7
|
+
# be faster and is potentially less error prone than running all of your
|
8
|
+
# migrations from scratch. Old migrations may fail to apply correctly if those
|
9
|
+
# migrations use external dependencies or application code.
|
10
|
+
#
|
11
|
+
# It's strongly recommended that you check this file into your version control system.
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define(version: 2021_01_06_060335) do
|
14
|
+
|
15
|
+
create_table "accounts", force: :cascade do |t|
|
16
|
+
t.boolean "tos_agreed", default: true, null: false
|
17
|
+
t.text "public_key", null: false
|
18
|
+
t.text "contacts", null: false
|
19
|
+
t.datetime "created_at", precision: 6, null: false
|
20
|
+
t.datetime "updated_at", precision: 6, null: false
|
21
|
+
t.index ["public_key"], name: "index_accounts_on_public_key", unique: true
|
22
|
+
t.index ["tos_agreed"], name: "index_accounts_on_tos_agreed"
|
23
|
+
end
|
24
|
+
|
25
|
+
create_table "authorizations", force: :cascade do |t|
|
26
|
+
t.string "status", default: "pending", null: false
|
27
|
+
t.datetime "expires", null: false
|
28
|
+
t.text "identifier", null: false
|
29
|
+
t.integer "order_id"
|
30
|
+
t.datetime "created_at", precision: 6, null: false
|
31
|
+
t.datetime "updated_at", precision: 6, null: false
|
32
|
+
t.index ["expires"], name: "index_authorizations_on_expires"
|
33
|
+
t.index ["order_id"], name: "index_authorizations_on_order_id"
|
34
|
+
t.index ["status"], name: "index_authorizations_on_status"
|
35
|
+
end
|
36
|
+
|
37
|
+
create_table "certificates", force: :cascade do |t|
|
38
|
+
t.string "subject", null: false
|
39
|
+
t.string "csr_fingerprint", null: false
|
40
|
+
t.text "data", null: false
|
41
|
+
t.text "alternate_names"
|
42
|
+
t.string "requester"
|
43
|
+
t.boolean "validated", default: false, null: false
|
44
|
+
t.integer "serial", null: false
|
45
|
+
t.datetime "created_at", precision: 6, null: false
|
46
|
+
t.datetime "updated_at", precision: 6, null: false
|
47
|
+
t.index ["csr_fingerprint"], name: "index_certificates_on_csr_fingerprint"
|
48
|
+
t.index ["serial"], name: "index_certificates_on_serial", unique: true
|
49
|
+
t.index ["subject"], name: "index_certificates_on_subject"
|
50
|
+
t.index ["validated"], name: "index_certificates_on_validated"
|
51
|
+
end
|
52
|
+
|
53
|
+
create_table "challenges", force: :cascade do |t|
|
54
|
+
t.string "acme_type", null: false
|
55
|
+
t.string "status", default: "pending", null: false
|
56
|
+
t.datetime "expires", null: false
|
57
|
+
t.string "token", null: false
|
58
|
+
t.datetime "validated"
|
59
|
+
t.integer "authorization_id"
|
60
|
+
t.datetime "created_at", precision: 6, null: false
|
61
|
+
t.datetime "updated_at", precision: 6, null: false
|
62
|
+
t.index ["acme_type"], name: "index_challenges_on_acme_type"
|
63
|
+
t.index ["authorization_id"], name: "index_challenges_on_authorization_id"
|
64
|
+
t.index ["expires"], name: "index_challenges_on_expires"
|
65
|
+
t.index ["status"], name: "index_challenges_on_status"
|
66
|
+
end
|
67
|
+
|
68
|
+
create_table "nonces", force: :cascade do |t|
|
69
|
+
t.string "token", null: false
|
70
|
+
t.datetime "created_at", precision: 6, null: false
|
71
|
+
t.datetime "updated_at", precision: 6, null: false
|
72
|
+
t.index ["token"], name: "index_nonces_on_token", unique: true
|
73
|
+
end
|
74
|
+
|
75
|
+
create_table "orders", force: :cascade do |t|
|
76
|
+
t.string "status", default: "pending", null: false
|
77
|
+
t.datetime "expires", null: false
|
78
|
+
t.text "identifiers", null: false
|
79
|
+
t.datetime "not_before", null: false
|
80
|
+
t.datetime "not_after", null: false
|
81
|
+
t.integer "certificate_id"
|
82
|
+
t.integer "account_id"
|
83
|
+
t.datetime "created_at", precision: 6, null: false
|
84
|
+
t.datetime "updated_at", precision: 6, null: false
|
85
|
+
t.index ["account_id"], name: "index_orders_on_account_id"
|
86
|
+
t.index ["certificate_id"], name: "index_orders_on_certificate_id"
|
87
|
+
t.index ["expires"], name: "index_orders_on_expires"
|
88
|
+
t.index ["status"], name: "index_orders_on_status"
|
89
|
+
end
|
90
|
+
|
91
|
+
add_foreign_key "orders", "certificates"
|
92
|
+
end
|
data/lib/bullion.rb
CHANGED
@@ -1,6 +1,97 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# Standard Library requirements
|
4
|
+
require 'base64'
|
5
|
+
require 'resolv'
|
6
|
+
require 'securerandom'
|
7
|
+
require 'time'
|
8
|
+
require 'logger'
|
9
|
+
require 'openssl'
|
10
|
+
|
11
|
+
# External requirements
|
12
|
+
require 'sinatra/base'
|
13
|
+
require 'sinatra/custom_logger'
|
14
|
+
require 'mysql2'
|
15
|
+
require 'sinatra/activerecord'
|
16
|
+
require 'jwt'
|
17
|
+
require 'prometheus/client'
|
18
|
+
require 'httparty'
|
19
|
+
|
20
|
+
# The top-level module for Bullion
|
3
21
|
module Bullion
|
4
22
|
class Error < StandardError; end
|
5
|
-
|
23
|
+
class ConfigError < Error; end
|
24
|
+
|
25
|
+
LOGGER = Logger.new($stdout)
|
26
|
+
|
27
|
+
# Config through environment variables
|
28
|
+
CA_DIR = File.expand_path ENV.fetch('CA_DIR', 'tmp')
|
29
|
+
CA_SECRET = ENV.fetch('CA_SECRET', 'SomeS3cret')
|
30
|
+
CA_KEY_PATH = ENV.fetch('CA_KEY_PATH') { File.join(CA_DIR, 'tls.key') }
|
31
|
+
CA_CERT_PATH = ENV.fetch('CA_CERT_PATH') { File.join(CA_DIR, 'tls.crt') }
|
32
|
+
CA_DOMAINS = ENV.fetch('CA_DOMAINS', 'example.com').split(',')
|
33
|
+
|
34
|
+
# Set up log level
|
35
|
+
LOGGER.level = ENV.fetch('LOG_LEVEL', :warn)
|
36
|
+
|
37
|
+
# 90 days cert expiration
|
38
|
+
CERT_VALIDITY_DURATION = Integer(
|
39
|
+
ENV.fetch('CERT_VALIDITY_DURATION', 60 * 60 * 24 * 30 * 3)
|
40
|
+
)
|
41
|
+
|
42
|
+
DB_CONNECTION_SETTINGS =
|
43
|
+
ENV['DATABASE_URL'] || {
|
44
|
+
adapter: 'mysql2',
|
45
|
+
database: ENV.fetch('DB_NAME', 'bullion'),
|
46
|
+
encoding: ENV.fetch('DB_ENCODING', 'utf8mb4'),
|
47
|
+
pool: Integer(ENV.fetch('MAX_THREADS', 32)),
|
48
|
+
username: ENV.fetch('DB_USERNAME', 'root'),
|
49
|
+
password: ENV['DB_PASSWORD'],
|
50
|
+
host: ENV.fetch('DB_HOST', 'localhost')
|
51
|
+
}
|
52
|
+
DB_CONNECTION_SETTINGS.freeze
|
53
|
+
|
54
|
+
NAMESERVERS = ENV.fetch('DNS01_NAMESERVERS', '8.8.8.8').split(',')
|
55
|
+
|
56
|
+
MetricsRegistry = Prometheus::Client.registry
|
57
|
+
|
58
|
+
def self.ca_key
|
59
|
+
@ca_key ||= OpenSSL::PKey::RSA.new(File.read(CA_KEY_PATH), CA_SECRET)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.ca_cert
|
63
|
+
@ca_cert ||= OpenSSL::X509::Certificate.new(File.read(CA_CERT_PATH))
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.rotate_keys!
|
67
|
+
@ca_key = nil
|
68
|
+
@ca_cert = nil
|
69
|
+
ca_key
|
70
|
+
ca_cert
|
71
|
+
true
|
72
|
+
end
|
73
|
+
|
74
|
+
# Ensures configuration settings are valid
|
75
|
+
# @see https://support.apple.com/en-us/HT211025
|
76
|
+
def self.validate_config!
|
77
|
+
raise ConfigError, 'Invalid Key Passphrase' unless CA_SECRET.is_a?(String)
|
78
|
+
raise ConfigError, "Invalid Key Path: #{CA_KEY_PATH}" unless File.readable?(CA_KEY_PATH)
|
79
|
+
raise ConfigError, "Invalid Cert Path: #{CA_CERT_PATH}" unless File.readable?(CA_CERT_PATH)
|
80
|
+
raise ConfigError, 'Cert Validity Too Long' if CERT_VALIDITY_DURATION > 60 * 60 * 24 * 397
|
81
|
+
raise ConfigError, 'Cert Validity Too Short' if CERT_VALIDITY_DURATION < 60 * 60 * 24 * 2
|
82
|
+
end
|
6
83
|
end
|
84
|
+
|
85
|
+
# Internal requirements
|
86
|
+
require 'bullion/version'
|
87
|
+
require 'bullion/acme/error'
|
88
|
+
require 'bullion/helpers/acme'
|
89
|
+
require 'bullion/helpers/service'
|
90
|
+
require 'bullion/helpers/ssl'
|
91
|
+
require 'bullion/models'
|
92
|
+
require 'bullion/service'
|
93
|
+
require 'bullion/services/ping'
|
94
|
+
require 'bullion/services/ca'
|
95
|
+
require 'bullion/challenge_client'
|
96
|
+
require 'bullion/challenge_clients/dns'
|
97
|
+
require 'bullion/challenge_clients/http'
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bullion
|
4
|
+
module Acme
|
5
|
+
# ACME protocol errors super class
|
6
|
+
class Error < Bullion::Error
|
7
|
+
# @see https://tools.ietf.org/html/rfc8555#section-6.7
|
8
|
+
def acme_type
|
9
|
+
'genericError'
|
10
|
+
end
|
11
|
+
|
12
|
+
def acme_preface
|
13
|
+
'urn:ietf:params:acme:error:'
|
14
|
+
end
|
15
|
+
|
16
|
+
def acme_error
|
17
|
+
acme_preface + acme_type
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Errors
|
22
|
+
# ACME exception for bad CSRs
|
23
|
+
class BadCsr < Bullion::Acme::Error
|
24
|
+
def acme_type
|
25
|
+
'badCSR'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# ACME exception for bad Nonces
|
30
|
+
class BadNonce < Bullion::Acme::Error
|
31
|
+
def acme_type
|
32
|
+
'badNonce'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# ACME exception for invalid contacts in accounts
|
37
|
+
class InvalidContact < Bullion::Acme::Error
|
38
|
+
def acme_type
|
39
|
+
'invalidContact'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# ACME exception for invalid orders
|
44
|
+
class InvalidOrder < Bullion::Acme::Error
|
45
|
+
def acme_type
|
46
|
+
'invalidOrder'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# ACME exception for malformed requests
|
51
|
+
class Malformed < Bullion::Acme::Error
|
52
|
+
def acme_type
|
53
|
+
'malformed'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# ACME exception for unsupported contacts in accounts
|
58
|
+
class UnsupportedContact < Bullion::Acme::Error
|
59
|
+
def acme_type
|
60
|
+
'unsupportedContact'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Non-standard exception for unsupported challenge types
|
65
|
+
class UnsupportedChallengeType < Bullion::Acme::Error
|
66
|
+
def acme_error
|
67
|
+
'urn:ietf:params:bullion:error:unsupportedChallengeType'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|