Skip to content
This repository has been archived by the owner on Aug 7, 2023. It is now read-only.

Generate ECDSA keypairs #19

Merged
merged 4 commits into from
Jan 25, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
293 changes: 182 additions & 111 deletions Gopkg.lock

Large diffs are not rendered by default.

1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,6 @@ data "bless_lambda" "code" {
resource "aws_lambda_function" "bless" {
filename = "${path.module}/bless.zip"
source_code_hash = "${data.bless_lambda.code.output_base64sha256}"

...
}
```
3 changes: 2 additions & 1 deletion pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ func Provider() *schema.Provider {
},
},
ResourcesMap: map[string]*schema.Resource{
"bless_ca": CA(),
"bless_ca": CA(),
"bless_ecdsa_ca": ECDSACA(),
},
DataSourcesMap: map[string]*schema.Resource{
"bless_lambda": Lambda(),
Expand Down
1 change: 0 additions & 1 deletion pkg/provider/provider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"testing"

"github.com/aws/aws-sdk-go/service/kms/kmsiface"

"github.com/aws/aws-sdk-go/service/kms"
"github.com/chanzuckerberg/terraform-provider-bless/pkg/aws"
"github.com/chanzuckerberg/terraform-provider-bless/pkg/provider"
Expand Down
55 changes: 6 additions & 49 deletions pkg/provider/resource_ca.go
Original file line number Diff line number Diff line change
@@ -1,18 +1,13 @@
package provider

import (
"bytes"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/pem"

"github.com/chanzuckerberg/terraform-provider-bless/pkg/aws"
"github.com/chanzuckerberg/terraform-provider-bless/pkg/util"
"github.com/hashicorp/terraform/helper/schema"
"github.com/pkg/errors"
"golang.org/x/crypto/ssh"
)

const (
Expand Down Expand Up @@ -68,12 +63,6 @@ func newResourceCA() *resourceCA {
return &resourceCA{}
}

type keyPair struct {
publicKey string
b64EncryptedPrivateKey string
password []byte
}

// Create creates a CA
func (ca *resourceCA) Create(d *schema.ResourceData, meta interface{}) error {
awsClient, ok := meta.(*aws.Client)
Expand All @@ -86,15 +75,15 @@ func (ca *resourceCA) Create(d *schema.ResourceData, meta interface{}) error {
if err != nil {
return err
}
encryptedPassword, err := awsClient.KMS.EncryptBytes(keyPair.password, kmsKeyID)
encryptedPassword, err := awsClient.KMS.EncryptBytes(keyPair.Password, kmsKeyID)
if err != nil {
return err
}

d.Set(schemaEncryptedPrivateKey, keyPair.b64EncryptedPrivateKey)
d.Set(schemaPublicKey, keyPair.publicKey)
d.Set(schemaEncryptedPrivateKey, keyPair.B64EncryptedPrivateKey)
d.Set(schemaPublicKey, keyPair.PublicKey)
d.Set(schemaEncryptedPassword, encryptedPassword)
d.SetId(util.HashForState(keyPair.publicKey))
d.SetId(util.HashForState(keyPair.PublicKey))
return nil
}

Expand All @@ -110,43 +99,11 @@ func (ca *resourceCA) Delete(d *schema.ResourceData, meta interface{}) error {
}

// ------------ helpers ------------------
func (ca *resourceCA) createKeypair() (*keyPair, error) {
func (ca *resourceCA) createKeypair() (*util.CA, error) {
// generate private key
privateKey, err := rsa.GenerateKey(rand.Reader, keySize)
if err != nil {
return nil, errors.Wrap(err, "Private key generation failed")
}
block := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(privateKey),
}

// generate password
password, err := util.GenerateRandomBytes(caPasswordBytes)
if err != nil {
return nil, errors.Wrap(err, "Could not generate enough random bytes")
}

// encrypt the private key
block, err = x509.EncryptPEMBlock(rand.Reader, block.Type, block.Bytes, password, x509.PEMCipherAES256)
if err != nil {
return nil, errors.Wrap(err, "Failed to encrypt CA")
}
var encryptedPEMBytes bytes.Buffer
err = pem.Encode(&encryptedPEMBytes, block)
if err != nil {
return nil, errors.Wrap(err, "Could not PEM encode encrypted CA")
}

// public key in openssh format
sshPublicKey, err := ssh.NewPublicKey(&privateKey.PublicKey)
if err != nil {
return nil, errors.Wrap(err, "Could not generate openssh public key")
}

return &keyPair{
publicKey: string(ssh.MarshalAuthorizedKey(sshPublicKey)),
b64EncryptedPrivateKey: base64.StdEncoding.EncodeToString(encryptedPEMBytes.Bytes()),
password: password,
}, nil
return util.NewCA(privateKey, privateKey.Public(), caPasswordBytes)
}
3 changes: 3 additions & 0 deletions pkg/provider/resource_ca_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ func TestCreate(t *testing.T) {
a.Regexp(
regexp.MustCompile("^-----BEGIN RSA PRIVATE KEY-----"),
string(bytesPrivate))
a.Regexp(
regexp.MustCompile(`AES-256-CBC`),
string(bytesPrivate))

publicSSHUntyped := s.RootModule().Outputs["public_key"].Value
publicSSH, ok := publicSSHUntyped.(string)
Expand Down
101 changes: 101 additions & 0 deletions pkg/provider/resource_ecdsa_ca.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
package provider

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"

"github.com/chanzuckerberg/terraform-provider-bless/pkg/aws"
"github.com/chanzuckerberg/terraform-provider-bless/pkg/util"
"github.com/hashicorp/terraform/helper/schema"
"github.com/pkg/errors"
)

// ECDSACA is an ecdsa CA resource
func ECDSACA() *schema.Resource {
ca := newResourceECDSACA()
return &schema.Resource{
Create: ca.Create,
Read: ca.Read,
Delete: ca.Delete,

Schema: map[string]*schema.Schema{
schemaKmsKeyID: &schema.Schema{
Type: schema.TypeString,
Required: true,
Description: "The kms key with which we should encrypt the CA password.",
ForceNew: true,
},

// computed
schemaEncryptedPrivateKey: &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "This is the base64 encoded CA encrypted private key.",
},
schemaPublicKey: &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "This is the plaintext CA public key in openssh format.",
},
schemaEncryptedPassword: &schema.Schema{
Type: schema.TypeString,
Computed: true,
Description: "This is the kms encrypted password.",
},
},
}
}

// resourceCA is a namespace
type resourceECDSACA struct{}

func newResourceECDSACA() *resourceECDSACA {
return &resourceECDSACA{}
}

// Create creates a CA
func (ca *resourceECDSACA) Create(d *schema.ResourceData, meta interface{}) error {
awsClient, ok := meta.(*aws.Client)
if !ok {
return errors.New("meta is not of type *aws.Client")
}

kmsKeyID := d.Get(schemaKmsKeyID).(string)
keyPair, err := ca.createKeypair()
if err != nil {
return err
}

encryptedPassword, err := awsClient.KMS.EncryptBytes(keyPair.Password, kmsKeyID)
if err != nil {
return err
}

d.Set(schemaEncryptedPrivateKey, keyPair.B64EncryptedPrivateKey)
d.Set(schemaPublicKey, keyPair.PublicKey)
d.Set(schemaEncryptedPassword, encryptedPassword)
d.SetId(util.HashForState(keyPair.PublicKey))
return nil
}

// Read reads the ca
func (ca *resourceECDSACA) Read(d *schema.ResourceData, meta interface{}) error {
return nil
}

// Delete deletes the ca
func (ca *resourceECDSACA) Delete(d *schema.ResourceData, meta interface{}) error {
d.SetId("")
return nil
}

// ------------ helpers ------------------
func (ca *resourceECDSACA) createKeypair() (*util.CA, error) {
// generate private key
privateKey, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
if err != nil {
return nil, errors.Wrap(err, "Private key generation failed")
}
return util.NewCA(privateKey, privateKey.Public(), caPasswordBytes)
}
74 changes: 74 additions & 0 deletions pkg/provider/resource_ecdsa_ca_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package provider_test

import (
"crypto/rand"
"encoding/base64"
"regexp"
"testing"

"github.com/aws/aws-sdk-go/service/kms"
r "github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)

func TestCreateECDSA(t *testing.T) {
a := assert.New(t)
providers, kmsMock := getTestProviders()

ciphertext := make([]byte, 10)
rand.Read(ciphertext)
output := &kms.EncryptOutput{
CiphertextBlob: ciphertext,
}
kmsMock.On("Encrypt", mock.Anything).Return(output, nil)

r.Test(t, r.TestCase{
Providers: providers,
Steps: []r.TestStep{
r.TestStep{
Config: `
provider "bless" {
region = "us-east-1"
}

resource "bless_ecdsa_ca" "bless" {
kms_key_id = "testo"
}

output "ecdsa_private_key" {
value = "${bless_ecdsa_ca.bless.encrypted_ca}"
}
output "ecdsa_public_key" {
value = "${bless_ecdsa_ca.bless.public_key}"
}
output "ecdsa_password" {
value = "${bless_ecdsa_ca.bless.encrypted_password}"
}
`,
Check: func(s *terraform.State) error {
privateUntyped := s.RootModule().Outputs["ecdsa_private_key"].Value
private, ok := privateUntyped.(string)
a.True(ok)
bytesPrivate, err := base64.StdEncoding.DecodeString(private)
a.Nil(err)
a.Regexp(
regexp.MustCompile("^-----BEGIN EC PRIVATE KEY-----"),
string(bytesPrivate))
a.Regexp(
regexp.MustCompile(`AES-256-CBC`),
string(bytesPrivate))
publicSSHUntyped := s.RootModule().Outputs["ecdsa_public_key"].Value
publicSSH, ok := publicSSHUntyped.(string)
a.True(ok)
a.Regexp(
regexp.MustCompile("^ecdsa-sha2-nistp521 "),
string(publicSSH))
return nil
},
},
},
})
}

Loading