Skip to content

Commit

Permalink
Added RSA message signing (#16)
Browse files Browse the repository at this point in the history
* Added initial RSA signing logic

* Allowed export of RSA key

* Added check for Signing Algorithm validity

* Added tests for RSA signing

* Updated README

* Readme formatting

* README update

* Added RSA guide

* Amended warning
  • Loading branch information
nsmithuk authored Jun 16, 2020
1 parent 94f5ffb commit 6bbbc91
Show file tree
Hide file tree
Showing 12 changed files with 2,821 additions and 86 deletions.
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@

A mock version of AWS' Key Management Service, for local development and testing. Written in Go.

_Whilst this service does use [real encryption](https://golang.org/pkg/crypto/aes/), it is designed for
_Whilst this project does use real encryption ([AES](https://golang.org/pkg/crypto/aes/), [ECDSA](https://golang.org/pkg/crypto/ecdsa/) and [RSA](https://golang.org/pkg/crypto/rsa/)), it is designed for
development and testing against KMS; not for use in a production environment._

#### (Local) KMS Usage Guides
* [Using AWS KMS via the CLI with a Symmetric Key](https://nsmith.net/aws-kms-cli)
* [Using AWS KMS via the CLI with Elliptic Curve (ECC) Keys](https://nsmith.net/aws-kms-cli-ecc)
* [Using AWS KMS via the CLI with RSA Keys for Message Signing](https://nsmith.net/aws-kms-cli-rsa-signing)

## Features

### Supports

* Symmetric and ECC_NIST keys
* Symmetric (AES) keys
* Asymmetric keys
* ECC_NIST keys
* RSA keys with a usage set to SIGN_VERIFY
* Management of Customer Master Keys; including:
* Enabling and disabling keys
* Scheduling key deletion
Expand All @@ -32,7 +40,8 @@ If a key in the seeding file already exists, it will not be overwritten or amend

### Does not (yet) support

* RSA or ECC_SECG_P256K1 keys
* RSA keys with a usage set to ENCRYPT_DECRYPT
* ECC_SECG_P256K1 keys
* Grants
* Importing your own key material
* Operations relating to a Custom Key Store
Expand Down Expand Up @@ -177,7 +186,9 @@ Local KMS runs on port http://localhost:8080 by default.

### Using LKMS with the CLI

For a more in-depth guide to theses commands, please see [Using AWS KMS via the CLI](https://nsmith.net/aws-kms-cli).
For a more in-depth guide to these commands, please see:
* [Using AWS KMS via the CLI with a Symmetric Key](https://nsmith.net/aws-kms-cli)
* [Using AWS KMS via the CLI with Elliptic Curve (ECC) Keys](https://nsmith.net/aws-kms-cli-ecc)

The examples here use `awslocal`, which wraps the `aws` command to include the required endpoint.

Expand Down
51 changes: 4 additions & 47 deletions src/cmk/ecc_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,14 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/sha256"
"crypto/sha512"
"encoding/asn1"
"encoding/json"
"errors"
"fmt"
"hash"
"math/big"
"os"
)

//---

type InvalidSigningAlgorithm struct {}
func (v *InvalidSigningAlgorithm) Error() string {
return "invalid signing algorithm"
}

//---

type InvalidDigestLength struct {}
func (v *InvalidDigestLength) Error() string {
return "invalid digest length"
}

//---

// We create our own type to manage JSON Marshaling
type EcdsaPrivateKey ecdsa.PrivateKey

Expand Down Expand Up @@ -164,7 +145,7 @@ func (k *EccKey) Sign(digest []byte, algorithm SigningAlgorithm) ([]byte, error)

func (k *EccKey) HashAndSign(message []byte, algorithm SigningAlgorithm) ([]byte, error) {

digest, err := k.hash(message, algorithm)
digest, err := hashMessage(message, algorithm)
if err != nil {
return []byte{}, err
}
Expand All @@ -174,7 +155,7 @@ func (k *EccKey) HashAndSign(message []byte, algorithm SigningAlgorithm) ([]byte

//----------------------------------------------------

func (k *EccKey) Verify(signature []byte, digest []byte) (bool, error) {
func (k *EccKey) Verify(signature []byte, digest []byte, algorithm SigningAlgorithm) (bool, error) {

ecdsaSignature := ecdsaSignature{}

Expand All @@ -192,36 +173,12 @@ func (k *EccKey) Verify(signature []byte, digest []byte) (bool, error) {

func (k *EccKey) HashAndVerify(signature []byte, message []byte, algorithm SigningAlgorithm) (bool, error) {

digest, err := k.hash(message, algorithm)
digest, err := hashMessage(message, algorithm)
if err != nil {
return false, err
}

return k.Verify(signature, digest)
}

//----------------------------------------------------

func (k *EccKey) hash(message []byte, algorithm SigningAlgorithm) ([]byte, error) {

//--------------------------
// Hash the message

var digest hash.Hash

switch algorithm {
case SigningAlgorithmEcdsaSha256:
digest = sha256.New()
case SigningAlgorithmEcdsaSha384:
digest = sha512.New384()
case SigningAlgorithmEcdsaSha512:
digest = sha512.New()
default:
return []byte{}, errors.New("unknown signing algorithm")
}

digest.Write(message)
return digest.Sum(nil), nil
return k.Verify(signature, digest, algorithm)
}

//----------------------------------------------------
Expand Down
15 changes: 15 additions & 0 deletions src/cmk/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cmk

type InvalidSigningAlgorithm struct {}
func (v *InvalidSigningAlgorithm) Error() string {
return "invalid signing algorithm"
}

//---

type InvalidDigestLength struct {}
func (v *InvalidDigestLength) Error() string {
return "invalid digest length"
}


30 changes: 30 additions & 0 deletions src/cmk/hash.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package cmk

import (
"crypto/sha256"
"crypto/sha512"
"errors"
"hash"
)

func hashMessage(message []byte, algorithm SigningAlgorithm) ([]byte, error) {

//--------------------------
// Hash the message

var digest hash.Hash

switch algorithm {
case SigningAlgorithmEcdsaSha256, SigningAlgorithmRsaPssSha256, SigningAlgorithmRsaPkcsSha256:
digest = sha256.New()
case SigningAlgorithmEcdsaSha384, SigningAlgorithmRsaPssSha384, SigningAlgorithmRsaPkcsSha384:
digest = sha512.New384()
case SigningAlgorithmEcdsaSha512, SigningAlgorithmRsaPssSha512, SigningAlgorithmRsaPkcsSha512:
digest = sha512.New()
default:
return []byte{}, errors.New("unknown signing algorithm")
}

digest.Write(message)
return digest.Sum(nil), nil
}
25 changes: 11 additions & 14 deletions src/cmk/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,11 @@ const (
SpecEccNistP256 CustomerMasterKeySpec = "ECC_NIST_P256"
SpecEccNistP384 CustomerMasterKeySpec = "ECC_NIST_P384"
SpecEccNistP521 CustomerMasterKeySpec = "ECC_NIST_P521"
SpecRsa2048 CustomerMasterKeySpec = "RSA_2048"
SpecRsa3072 CustomerMasterKeySpec = "RSA_3072"
SpecRsa4096 CustomerMasterKeySpec = "RSA_4096"
)

func IsValidSpec(t string) bool {
needle := CustomerMasterKeySpec(t)
haystack := []CustomerMasterKeySpec{SpecSymmetricDefault, SpecEccNistP256, SpecEccNistP384, SpecEccNistP521}

for _, v := range haystack {
if v == needle {
return true
}
}
return false
}

//---

type EncryptionAlgorithm string
Expand All @@ -45,6 +36,12 @@ const (
SigningAlgorithmEcdsaSha256 SigningAlgorithm = "ECDSA_SHA_256"
SigningAlgorithmEcdsaSha384 SigningAlgorithm = "ECDSA_SHA_384"
SigningAlgorithmEcdsaSha512 SigningAlgorithm = "ECDSA_SHA_512"
SigningAlgorithmRsaPssSha256 SigningAlgorithm = "RSASSA_PSS_SHA_256"
SigningAlgorithmRsaPssSha384 SigningAlgorithm = "RSASSA_PSS_SHA_384"
SigningAlgorithmRsaPssSha512 SigningAlgorithm = "RSASSA_PSS_SHA_512"
SigningAlgorithmRsaPkcsSha256 SigningAlgorithm = "RSASSA_PKCS1_V1_5_SHA_256"
SigningAlgorithmRsaPkcsSha384 SigningAlgorithm = "RSASSA_PKCS1_V1_5_SHA_384"
SigningAlgorithmRsaPkcsSha512 SigningAlgorithm = "RSASSA_PKCS1_V1_5_SHA_512"
)

//---
Expand All @@ -68,7 +65,7 @@ type SigningKey interface {
Key
Sign(digest []byte, algorithm SigningAlgorithm) ([]byte, error)
HashAndSign(message []byte, algorithm SigningAlgorithm) ([]byte, error)
Verify(signature []byte, digest []byte) (bool, error)
Verify(signature []byte, digest []byte, algorithm SigningAlgorithm) (bool, error)
HashAndVerify(signature []byte, digest []byte, algorithm SigningAlgorithm) (bool, error)
}

Expand All @@ -95,7 +92,7 @@ type KeyMetadata struct {
Origin string `json:",omitempty"`
ValidTo int64 `json:",omitempty"`

SigningAlgorithms []SigningAlgorithm `json:",omitempty"`
SigningAlgorithms []SigningAlgorithm `json:",omitempty"`
EncryptionAlgorithms []EncryptionAlgorithm `json:",omitempty"`
CustomerMasterKeySpec CustomerMasterKeySpec `json:",omitempty"`
}
Loading

0 comments on commit 6bbbc91

Please sign in to comment.