Skip to content

Commit

Permalink
kms additions (#731)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
geemus authored Dec 17, 2024
1 parent a7f4e17 commit 43a6fd5
Show file tree
Hide file tree
Showing 12 changed files with 468 additions and 69 deletions.
6 changes: 5 additions & 1 deletion lib/fog/aws/kms.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class KMS < Fog::Service
request :list_keys
request :create_key
request :describe_key
request :get_public_key
request :schedule_key_deletion
request :sign

model_path 'fog/aws/models/kms'
model :key
Expand All @@ -30,7 +33,8 @@ def self.data
@data ||= Hash.new do |hash, region|
hash[region] = Hash.new do |region_hash, access_key|
region_hash[access_key] = {
:keys => {},
keys: {},
pkeys: {}
}
end
end
Expand Down
4 changes: 2 additions & 2 deletions lib/fog/aws/parsers/kms/describe_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ def start_element(name, attrs = [])

def end_element(name)
case name
when 'KeyUsage', 'AWSAccountId', 'Description', 'KeyId', 'Arn'
when 'Arn', 'AWSAccountId', 'Description', 'KeyId', 'KeySpec', 'KeyState', 'KeyUsage'
@key[name] = value
when 'CreationDate'
when 'CreationDate', 'DeletionDate'
@key[name] = Time.parse(value)
when 'Enabled'
@key[name] = (value == 'true')
Expand Down
30 changes: 30 additions & 0 deletions lib/fog/aws/parsers/kms/get_public_key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
module Fog
module Parsers
module AWS
module KMS
class GetPublicKey < Fog::Parsers::Base
def reset
@response = {}
end

def start_element(name, attrs = [])
super
case name
when 'EncryptionAlgorithms', 'KeyAgreementAlgorithms', 'SigningAlgorithms'
@response[name] = []
end
end

def end_element(name)
case name
when 'KeyId', 'KeySpec', 'KeyUsage', 'PublicKey'
@response[name] = value
when 'EncryptionAlgorithms', 'KeyAgreementAlgorithms', 'SigningAlgorithms'
@response[name] << value
end
end
end
end
end
end
end
28 changes: 28 additions & 0 deletions lib/fog/aws/parsers/kms/schedule_key_deletion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module Fog
module Parsers
module AWS
module KMS
class ScheduleKeyDeletion < Fog::Parsers::Base
def reset
@response = {}
end

def start_element(name, attrs = [])
super
end

def end_element(name)
case name
when 'DeletionDate'
@response[name] = Time.parse(value)
when 'KeyId', 'KeyState'
@response[name] = value
when 'PendingWindowInDays'
@response[name] = value.to_i
end
end
end
end
end
end
end
24 changes: 24 additions & 0 deletions lib/fog/aws/parsers/kms/sign.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
module Fog
module Parsers
module AWS
module KMS
class Sign < Fog::Parsers::Base
def reset
@response = {}
end

def start_element(name, attrs = [])
super
end

def end_element(name)
case name
when 'KeyId', 'Signature', 'SigningAlgorithm'
@response[name] = value
end
end
end
end
end
end
end
110 changes: 74 additions & 36 deletions lib/fog/aws/requests/kms/create_key.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,61 +2,99 @@ module Fog
module AWS
class KMS
class Real
DEFAULT_KEY_POLICY = <<-JSON
{
"Version": "2012-10-17",
"Id": "key-default-1",
"Statement": [
{
"Sid": "Enable IAM User Permissions",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::915445820265:root"
},
"Action": "kms:*",
"Resource": "*"
}
]
}
JSON

require 'fog/aws/parsers/kms/describe_key'

def create_key(policy = nil, description = nil, usage = "ENCRYPT_DECRYPT")
request(
'Action' => 'CreateKey',
'Description' => description,
'KeyUsage' => usage,
'Policy' => policy,
:parser => Fog::Parsers::AWS::KMS::DescribeKey.new
)
# Create Key
#
# ==== Parameters
# * options<~Hash>:
# * 'Description'<~String>:
# * 'KeyUsage'<~String>:
# * 'Policy'<~String>:
# * ... (see docs from see also)
#
# === Returns
#
# ==== See Also
# https://docs.aws.amazon.com/kms/latest/APIReference/API_CreateKey.html
def create_key(*args)
options = Fog::AWS::KMS.parse_create_key_args(args)
request({
'Action' => 'CreateKey',
:parser => Fog::Parsers::AWS::KMS::DescribeKey.new
}.merge!(options))
end
end

class Mock
def create_key(policy = nil, description = nil, usage = "ENCRYPT_DECRYPT")
def create_key(*args)
options = Fog::AWS::KMS.parse_create_key_args(args)

response = Excon::Response.new
key_id = UUID.uuid
key_arn = Fog::AWS::Mock.arn("kms", self.account_id, "key/#{key_id}", @region)

key = {
"KeyUsage" => usage,
"AWSAccountId" => self.account_id,
"KeyId" => key_id,
"Description" => description,
"CreationDate" => Time.now,
"Arn" => key_arn,
"Enabled" => true,
}
'Arn' => key_arn,
'AWSAccountId' => self.account_id,
'CreationDate' => Time.now.utc,
'DeletionDate' => nil,
'Description' => nil,
'Enabled' => true,
'KeyId' => key_id,
'KeySpec' => 'SYMMETRIC_DEFAULT',
'KeyState' => 'Enabled',
'KeyUsage' => 'ENCRYPT_DECRYPT',
'Policy' => nil
}.merge!(options)

# @todo use default policy

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

response.body = { "KeyMetadata" => key }
klass, arg = {
'ECC_NIST_P256' => [OpenSSL::PKey::EC, 'prime256v1'],
'ECC_NIST_P384' => [OpenSSL::PKey::EC, 'secp384r1'],
'ECC_NIST_P521' => [OpenSSL::PKey::EC, 'secp521r1'],
'ECC_SECG_P256K1' => [OpenSSL::PKey::EC, 'secp256k1'],
'RSA_2048' => [OpenSSL::PKey::RSA, 2048],
'RSA_3072' => [OpenSSL::PKey::RSA, 3072],
'RSA_4096' => [OpenSSL::PKey::RSA, 4096]
}[key['KeySpec']]
raise "Unknown or not-yet-implemented #{key['KeySpec']} KeySpec for kms create_key mocks" unless klass

self.data[:pkeys][key_id] = klass.generate(arg)

response.body = { 'KeyMetadata' => key }
response
end
end

# previous args (policy, description, usage) was deprecated in favor of a hash of options
def self.parse_create_key_args(args)
case args.size
when 0
{}
when 1
if args[0].is_a?(Hash)
args[0]
else
Fog::Logger.deprecation("create_key with distinct arguments is deprecated, use options hash instead [light_black](#{caller.first})[/]")
{
'Policy' => args[0]
}
end
when 2, 3
Fog::Logger.deprecation("create_key with distinct arguments is deprecated, use options hash instead [light_black](#{caller.first})[/]")
{
'Policy' => args[0],
'Description' => args[1],
'KeyUsage' => args[2] || 'ENCRYPT_DECRYPT'
}
else
raise "Unknown argument style: #{args.inspect}, use options hash instead."
end
end
end
end
end
35 changes: 35 additions & 0 deletions lib/fog/aws/requests/kms/get_public_key.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
module Fog
module AWS
class KMS
class Real
require 'fog/aws/parsers/kms/get_public_key'

def get_public_key(identifier, grant_tokens = nil)
request(
'Action' => 'GetPublicKey',
'GrantTokens' => grant_tokens,
'KeyId' => identifier,
:parser => Fog::Parsers::AWS::KMS::GetPublicKey.new
)
end
end

class Mock
def get_public_key(identifier, _grant_tokens = [])
response = Excon::Response.new
key = self.data[:keys][identifier]
pkey = self.data[:pkeys][identifier]

response.body = {
'KeyId' => key['Arn'],
'KeyUsage' => key['KeyUsage'],
'KeySpec' => key['KeySpec'],
'PublicKey' => Base64.strict_encode64(pkey.public_to_der),
'SigningAlgorithms' => key['SigningAlgorithms']
}
response
end
end
end
end
end
7 changes: 3 additions & 4 deletions lib/fog/aws/requests/kms/list_keys.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ module Fog
module AWS
class KMS
class Real

require 'fog/aws/parsers/kms/list_keys'

def list_keys(options={})
Expand Down Expand Up @@ -43,9 +42,9 @@ def list_keys(options={})
key_set = if marker
self.data[:markers][marker] || []
else
self.data[:keys].inject([]) { |r,(k,v)|
r << { "KeyId" => k, "KeyArn" => v["Arn"] }
}
self.data[:keys].inject([]) do |r, (k, v)|
r << { 'KeyArn' => v['Arn'], 'KeyId' => k }
end
end

keys = if limit
Expand Down
37 changes: 37 additions & 0 deletions lib/fog/aws/requests/kms/schedule_key_deletion.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
module Fog
module AWS
class KMS
class Real
require 'fog/aws/parsers/kms/schedule_key_deletion'

def schedule_key_deletion(identifier, pending_window_in_days)
request(
'Action' => 'ScheduleKeyDeletion',
'KeyId' => identifier,
'PendingWindowInDays' => pending_window_in_days,
:parser => Fog::Parsers::AWS::KMS::ScheduleKeyDeletion.new
)
end
end

class Mock
def schedule_key_deletion(identifier, pending_window_in_days)
response = Excon::Response.new
key = self.data[:keys][identifier]

key['DeletionDate'] = Time.now + (60 * 60 * 24 * pending_window_in_days)
key['Enabled'] = false
key['KeyState'] = 'PendingDeletion'

response.body = {
'DeletionDate' => key['DeletionDate'],
'KeyId' => key['KeyId'],
'KeyState' => key['KeyState'],
'PendingWindowInDays' => pending_window_in_days
}
response
end
end
end
end
end
Loading

0 comments on commit 43a6fd5

Please sign in to comment.