Skip to content

Commit 43a6fd5

Browse files
authored
kms additions (#731)
* add kms test lint fixes * remove unused default key policy constant from KMS create key request * minor cleanup and add first pass at schedule_key_deletion method and mock * fix for schedule_key_deletion test * [kms] convert create_key to options hash instead of positional arguments * fix mock data after options hash change * add KeySpec to create_key mock and expected format in tests * first pass at get_public_key requests and mocks * fix params for create_key in tests so that public_key/signing mocks will match * size for rsa key should be cast to integer * add mocks for ECC pkeys * first pass at kms sign request and mocks * simplify by using sign instead of sign_raw * switch to sign_pss, hopefully compatible with 3.0 * fix ec curve mapping * kms: mocks - cleanup signopts, support both raw and digest signing * starting to flesh out mock tests around signing/verification * further fleshing out/refining tests * add mock table tests for signing * get_public_key KeyId is actually ARN, also delete after sign table test, just in case * add missing keyspec to describe_key parser * add overlooked base64 encode to sign request calls * add a digest test to live+mock key tests as well
1 parent a7f4e17 commit 43a6fd5

File tree

12 files changed

+468
-69
lines changed

12 files changed

+468
-69
lines changed

lib/fog/aws/kms.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ class KMS < Fog::Service
2020
request :list_keys
2121
request :create_key
2222
request :describe_key
23+
request :get_public_key
24+
request :schedule_key_deletion
25+
request :sign
2326

2427
model_path 'fog/aws/models/kms'
2528
model :key
@@ -30,7 +33,8 @@ def self.data
3033
@data ||= Hash.new do |hash, region|
3134
hash[region] = Hash.new do |region_hash, access_key|
3235
region_hash[access_key] = {
33-
:keys => {},
36+
keys: {},
37+
pkeys: {}
3438
}
3539
end
3640
end

lib/fog/aws/parsers/kms/describe_key.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,9 @@ def start_element(name, attrs = [])
1717

1818
def end_element(name)
1919
case name
20-
when 'KeyUsage', 'AWSAccountId', 'Description', 'KeyId', 'Arn'
20+
when 'Arn', 'AWSAccountId', 'Description', 'KeyId', 'KeySpec', 'KeyState', 'KeyUsage'
2121
@key[name] = value
22-
when 'CreationDate'
22+
when 'CreationDate', 'DeletionDate'
2323
@key[name] = Time.parse(value)
2424
when 'Enabled'
2525
@key[name] = (value == 'true')
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
module Fog
2+
module Parsers
3+
module AWS
4+
module KMS
5+
class GetPublicKey < Fog::Parsers::Base
6+
def reset
7+
@response = {}
8+
end
9+
10+
def start_element(name, attrs = [])
11+
super
12+
case name
13+
when 'EncryptionAlgorithms', 'KeyAgreementAlgorithms', 'SigningAlgorithms'
14+
@response[name] = []
15+
end
16+
end
17+
18+
def end_element(name)
19+
case name
20+
when 'KeyId', 'KeySpec', 'KeyUsage', 'PublicKey'
21+
@response[name] = value
22+
when 'EncryptionAlgorithms', 'KeyAgreementAlgorithms', 'SigningAlgorithms'
23+
@response[name] << value
24+
end
25+
end
26+
end
27+
end
28+
end
29+
end
30+
end
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
module Fog
2+
module Parsers
3+
module AWS
4+
module KMS
5+
class ScheduleKeyDeletion < Fog::Parsers::Base
6+
def reset
7+
@response = {}
8+
end
9+
10+
def start_element(name, attrs = [])
11+
super
12+
end
13+
14+
def end_element(name)
15+
case name
16+
when 'DeletionDate'
17+
@response[name] = Time.parse(value)
18+
when 'KeyId', 'KeyState'
19+
@response[name] = value
20+
when 'PendingWindowInDays'
21+
@response[name] = value.to_i
22+
end
23+
end
24+
end
25+
end
26+
end
27+
end
28+
end

lib/fog/aws/parsers/kms/sign.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
module Fog
2+
module Parsers
3+
module AWS
4+
module KMS
5+
class Sign < Fog::Parsers::Base
6+
def reset
7+
@response = {}
8+
end
9+
10+
def start_element(name, attrs = [])
11+
super
12+
end
13+
14+
def end_element(name)
15+
case name
16+
when 'KeyId', 'Signature', 'SigningAlgorithm'
17+
@response[name] = value
18+
end
19+
end
20+
end
21+
end
22+
end
23+
end
24+
end

lib/fog/aws/requests/kms/create_key.rb

Lines changed: 74 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,61 +2,99 @@ module Fog
22
module AWS
33
class KMS
44
class Real
5-
DEFAULT_KEY_POLICY = <<-JSON
6-
{
7-
"Version": "2012-10-17",
8-
"Id": "key-default-1",
9-
"Statement": [
10-
{
11-
"Sid": "Enable IAM User Permissions",
12-
"Effect": "Allow",
13-
"Principal": {
14-
"AWS": "arn:aws:iam::915445820265:root"
15-
},
16-
"Action": "kms:*",
17-
"Resource": "*"
18-
}
19-
]
20-
}
21-
JSON
22-
235
require 'fog/aws/parsers/kms/describe_key'
246

25-
def create_key(policy = nil, description = nil, usage = "ENCRYPT_DECRYPT")
26-
request(
27-
'Action' => 'CreateKey',
28-
'Description' => description,
29-
'KeyUsage' => usage,
30-
'Policy' => policy,
31-
:parser => Fog::Parsers::AWS::KMS::DescribeKey.new
32-
)
7+
# Create Key
8+
#
9+
# ==== Parameters
10+
# * options<~Hash>:
11+
# * 'Description'<~String>:
12+
# * 'KeyUsage'<~String>:
13+
# * 'Policy'<~String>:
14+
# * ... (see docs from see also)
15+
#
16+
# === Returns
17+
#
18+
# ==== See Also
19+
# https://docs.aws.amazon.com/kms/latest/APIReference/API_CreateKey.html
20+
def create_key(*args)
21+
options = Fog::AWS::KMS.parse_create_key_args(args)
22+
request({
23+
'Action' => 'CreateKey',
24+
:parser => Fog::Parsers::AWS::KMS::DescribeKey.new
25+
}.merge!(options))
3326
end
3427
end
3528

3629
class Mock
37-
def create_key(policy = nil, description = nil, usage = "ENCRYPT_DECRYPT")
30+
def create_key(*args)
31+
options = Fog::AWS::KMS.parse_create_key_args(args)
32+
3833
response = Excon::Response.new
3934
key_id = UUID.uuid
4035
key_arn = Fog::AWS::Mock.arn("kms", self.account_id, "key/#{key_id}", @region)
4136

4237
key = {
43-
"KeyUsage" => usage,
44-
"AWSAccountId" => self.account_id,
45-
"KeyId" => key_id,
46-
"Description" => description,
47-
"CreationDate" => Time.now,
48-
"Arn" => key_arn,
49-
"Enabled" => true,
50-
}
38+
'Arn' => key_arn,
39+
'AWSAccountId' => self.account_id,
40+
'CreationDate' => Time.now.utc,
41+
'DeletionDate' => nil,
42+
'Description' => nil,
43+
'Enabled' => true,
44+
'KeyId' => key_id,
45+
'KeySpec' => 'SYMMETRIC_DEFAULT',
46+
'KeyState' => 'Enabled',
47+
'KeyUsage' => 'ENCRYPT_DECRYPT',
48+
'Policy' => nil
49+
}.merge!(options)
5150

5251
# @todo use default policy
5352

5453
self.data[:keys][key_id] = key
5554

56-
response.body = { "KeyMetadata" => key }
55+
klass, arg = {
56+
'ECC_NIST_P256' => [OpenSSL::PKey::EC, 'prime256v1'],
57+
'ECC_NIST_P384' => [OpenSSL::PKey::EC, 'secp384r1'],
58+
'ECC_NIST_P521' => [OpenSSL::PKey::EC, 'secp521r1'],
59+
'ECC_SECG_P256K1' => [OpenSSL::PKey::EC, 'secp256k1'],
60+
'RSA_2048' => [OpenSSL::PKey::RSA, 2048],
61+
'RSA_3072' => [OpenSSL::PKey::RSA, 3072],
62+
'RSA_4096' => [OpenSSL::PKey::RSA, 4096]
63+
}[key['KeySpec']]
64+
raise "Unknown or not-yet-implemented #{key['KeySpec']} KeySpec for kms create_key mocks" unless klass
65+
66+
self.data[:pkeys][key_id] = klass.generate(arg)
67+
68+
response.body = { 'KeyMetadata' => key }
5769
response
5870
end
5971
end
72+
73+
# previous args (policy, description, usage) was deprecated in favor of a hash of options
74+
def self.parse_create_key_args(args)
75+
case args.size
76+
when 0
77+
{}
78+
when 1
79+
if args[0].is_a?(Hash)
80+
args[0]
81+
else
82+
Fog::Logger.deprecation("create_key with distinct arguments is deprecated, use options hash instead [light_black](#{caller.first})[/]")
83+
{
84+
'Policy' => args[0]
85+
}
86+
end
87+
when 2, 3
88+
Fog::Logger.deprecation("create_key with distinct arguments is deprecated, use options hash instead [light_black](#{caller.first})[/]")
89+
{
90+
'Policy' => args[0],
91+
'Description' => args[1],
92+
'KeyUsage' => args[2] || 'ENCRYPT_DECRYPT'
93+
}
94+
else
95+
raise "Unknown argument style: #{args.inspect}, use options hash instead."
96+
end
97+
end
6098
end
6199
end
62100
end
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
module Fog
2+
module AWS
3+
class KMS
4+
class Real
5+
require 'fog/aws/parsers/kms/get_public_key'
6+
7+
def get_public_key(identifier, grant_tokens = nil)
8+
request(
9+
'Action' => 'GetPublicKey',
10+
'GrantTokens' => grant_tokens,
11+
'KeyId' => identifier,
12+
:parser => Fog::Parsers::AWS::KMS::GetPublicKey.new
13+
)
14+
end
15+
end
16+
17+
class Mock
18+
def get_public_key(identifier, _grant_tokens = [])
19+
response = Excon::Response.new
20+
key = self.data[:keys][identifier]
21+
pkey = self.data[:pkeys][identifier]
22+
23+
response.body = {
24+
'KeyId' => key['Arn'],
25+
'KeyUsage' => key['KeyUsage'],
26+
'KeySpec' => key['KeySpec'],
27+
'PublicKey' => Base64.strict_encode64(pkey.public_to_der),
28+
'SigningAlgorithms' => key['SigningAlgorithms']
29+
}
30+
response
31+
end
32+
end
33+
end
34+
end
35+
end

lib/fog/aws/requests/kms/list_keys.rb

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ module Fog
22
module AWS
33
class KMS
44
class Real
5-
65
require 'fog/aws/parsers/kms/list_keys'
76

87
def list_keys(options={})
@@ -43,9 +42,9 @@ def list_keys(options={})
4342
key_set = if marker
4443
self.data[:markers][marker] || []
4544
else
46-
self.data[:keys].inject([]) { |r,(k,v)|
47-
r << { "KeyId" => k, "KeyArn" => v["Arn"] }
48-
}
45+
self.data[:keys].inject([]) do |r, (k, v)|
46+
r << { 'KeyArn' => v['Arn'], 'KeyId' => k }
47+
end
4948
end
5049

5150
keys = if limit
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
module Fog
2+
module AWS
3+
class KMS
4+
class Real
5+
require 'fog/aws/parsers/kms/schedule_key_deletion'
6+
7+
def schedule_key_deletion(identifier, pending_window_in_days)
8+
request(
9+
'Action' => 'ScheduleKeyDeletion',
10+
'KeyId' => identifier,
11+
'PendingWindowInDays' => pending_window_in_days,
12+
:parser => Fog::Parsers::AWS::KMS::ScheduleKeyDeletion.new
13+
)
14+
end
15+
end
16+
17+
class Mock
18+
def schedule_key_deletion(identifier, pending_window_in_days)
19+
response = Excon::Response.new
20+
key = self.data[:keys][identifier]
21+
22+
key['DeletionDate'] = Time.now + (60 * 60 * 24 * pending_window_in_days)
23+
key['Enabled'] = false
24+
key['KeyState'] = 'PendingDeletion'
25+
26+
response.body = {
27+
'DeletionDate' => key['DeletionDate'],
28+
'KeyId' => key['KeyId'],
29+
'KeyState' => key['KeyState'],
30+
'PendingWindowInDays' => pending_window_in_days
31+
}
32+
response
33+
end
34+
end
35+
end
36+
end
37+
end

0 commit comments

Comments
 (0)