summaryrefslogtreecommitdiff
path: root/crypto
diff options
context:
space:
mode:
authorJesper B. Rosenkilde <jbr@humppa.dk>2022-05-06 16:06:19 +0200
committerJesper B. Rosenkilde <jbr@humppa.dk>2022-05-06 16:06:19 +0200
commit89c0960c3c03d45a8db3ec9e6348e35301cb03ce (patch)
tree355420dc2de5cc2082e744efb00499bbd7c454a0 /crypto
parent4337ba7131e3d9bb00134046c08c2ad7e4b2148c (diff)
Add ECDSA generation functions to crypto funcs
ECDSAGenerateKey takes one of Go's supported named NIST curves and an argument and returns a newly generated EC private key PEM encoded. ECDSADerivePublicKey takes a PEM encoded EC private key and derives the corresponding public key, which is returned PEM encoded.
Diffstat (limited to 'crypto')
-rw-r--r--crypto/ecdsa.go72
-rw-r--r--crypto/ecdsa_test.go82
2 files changed, 154 insertions, 0 deletions
diff --git a/crypto/ecdsa.go b/crypto/ecdsa.go
new file mode 100644
index 00000000..16b8dcaa
--- /dev/null
+++ b/crypto/ecdsa.go
@@ -0,0 +1,72 @@
+package crypto
+
+import (
+ "bytes"
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/x509"
+ "encoding/pem"
+ "fmt"
+)
+
+// ECDSAGenerateKey -
+func ECDSAGenerateKey(curve string) ([]byte, error) {
+ var c elliptic.Curve
+
+ if curve == "P-224" {
+ c = elliptic.P224()
+ } else if curve == "P-256" {
+ c = elliptic.P256()
+ } else if curve == "P-384" {
+ c = elliptic.P384()
+ } else if curve == "P-521" {
+ c = elliptic.P521()
+ } else {
+ return nil, fmt.Errorf("unknow curve: %s", curve)
+ }
+
+ priv, err := ecdsa.GenerateKey(c, rand.Reader)
+ if err != nil {
+ return nil, fmt.Errorf("failed to generate ECDSA private key: %w", err)
+ }
+ der, err := x509.MarshalECPrivateKey(priv)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal ECDSA private key: %w", err)
+ }
+ block := &pem.Block{
+ Type: "EC PRIVATE KEY",
+ Bytes: der,
+ }
+ buf := &bytes.Buffer{}
+ err = pem.Encode(buf, block)
+ if err != nil {
+ return nil, fmt.Errorf("failed to encode generated ECDSA private key: pem encoding failed: %w", err)
+ }
+ return buf.Bytes(), nil
+}
+
+// ECDSADerivePublicKey -
+func ECDSADerivePublicKey(privatekey []byte) ([]byte, error) {
+ block, _ := pem.Decode(privatekey)
+ if block == nil {
+ return nil, fmt.Errorf("failed to read key: no key found")
+ }
+
+ priv, err := x509.ParseECPrivateKey(block.Bytes)
+ if err != nil {
+ return nil, fmt.Errorf("invalid private key: %w", err)
+ }
+
+ b, err := x509.MarshalPKIXPublicKey(&priv.PublicKey)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal PKIX public key: %w", err)
+ }
+
+ block = &pem.Block{
+ Type: "PUBLIC KEY",
+ Bytes: b,
+ }
+
+ return pem.EncodeToMemory(block), nil
+}
diff --git a/crypto/ecdsa_test.go b/crypto/ecdsa_test.go
new file mode 100644
index 00000000..815f7a23
--- /dev/null
+++ b/crypto/ecdsa_test.go
@@ -0,0 +1,82 @@
+package crypto
+
+import (
+ "crypto/ecdsa"
+ "crypto/elliptic"
+ "crypto/rand"
+ "crypto/x509"
+ "encoding/pem"
+ "strings"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func genECDSAPrivKey() (*ecdsa.PrivateKey, string) {
+ priv, _ := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
+ der, _ := x509.MarshalECPrivateKey(priv)
+ privBlock := &pem.Block{
+ Type: "EC PRIVATE KEY",
+ Bytes: der,
+ }
+ return priv, string(pem.EncodeToMemory(privBlock))
+}
+
+func deriveECPubkey(priv *ecdsa.PrivateKey) string {
+ b, _ := x509.MarshalPKIXPublicKey(&priv.PublicKey)
+ pubBlock := &pem.Block{
+ Type: "PUBLIC KEY",
+ Bytes: b,
+ }
+ testPubKey := string(pem.EncodeToMemory(pubBlock))
+ return testPubKey
+}
+
+func TestECDSAGenerateKey(t *testing.T) {
+ key, err := ECDSAGenerateKey("P-224")
+ assert.NoError(t, err)
+ assert.True(t, strings.HasPrefix(string(key),
+ "-----BEGIN EC PRIVATE KEY-----"))
+ assert.True(t, strings.HasSuffix(string(key),
+ "-----END EC PRIVATE KEY-----\n"))
+
+ key, err = ECDSAGenerateKey("P-256")
+ assert.NoError(t, err)
+ assert.True(t, strings.HasPrefix(string(key),
+ "-----BEGIN EC PRIVATE KEY-----"))
+ assert.True(t, strings.HasSuffix(string(key),
+ "-----END EC PRIVATE KEY-----\n"))
+
+ key, err = ECDSAGenerateKey("P-384")
+ assert.NoError(t, err)
+ assert.True(t, strings.HasPrefix(string(key),
+ "-----BEGIN EC PRIVATE KEY-----"))
+ assert.True(t, strings.HasSuffix(string(key),
+ "-----END EC PRIVATE KEY-----\n"))
+
+ key, err = ECDSAGenerateKey("P-521")
+ assert.NoError(t, err)
+ assert.True(t, strings.HasPrefix(string(key),
+ "-----BEGIN EC PRIVATE KEY-----"))
+ assert.True(t, strings.HasSuffix(string(key),
+ "-----END EC PRIVATE KEY-----\n"))
+
+ key, err = ECDSAGenerateKey("P-999")
+ assert.Error(t, err)
+}
+
+func TestECDSADerivePublicKey(t *testing.T) {
+ _, err := ECDSADerivePublicKey(nil)
+ assert.Error(t, err)
+
+ _, err = ECDSADerivePublicKey([]byte(`-----BEGIN FOO-----
+ -----END FOO-----`))
+ assert.Error(t, err)
+
+ priv, privKey := genECDSAPrivKey()
+ expected := deriveECPubkey(priv)
+
+ actual, err := ECDSADerivePublicKey([]byte(privKey))
+ assert.NoError(t, err)
+ assert.Equal(t, expected, string(actual))
+}