Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxec
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
Expand Down Expand Up @@ -218,6 +219,7 @@ github.com/peterh/liner v1.2.2 h1:aJ4AOodmL+JxOZZEL2u9iJf8omNRpqHc/EbrK+3mAXw=
github.com/peterh/liner v1.2.2/go.mod h1:xFwJyiKIXJZUKItq5dGHZSTBRAuG/CpeNpWLyiNRNwI=
github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo=
github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
Expand Down
99 changes: 99 additions & 0 deletions lib/ocrypto/ec_key_pair.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package ocrypto

import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
"errors"
"fmt"
)

type ECCMode uint8

const (
ECCModeSecp256r1 ECCMode = 0
ECCModeSecp384r1 ECCMode = 1
ECCModeSecp521r1 ECCMode = 2
ECCModeSecp256k1 ECCMode = 3
)

type ECKeyPair struct {
PrivateKey *ecdsa.PrivateKey
}

// NewECKeyPair Generates an EC key pair of the given bit size.
func NewECKeyPair(mode ECCMode) (ECKeyPair, error) {
var c elliptic.Curve
switch mode {
case ECCModeSecp256r1:
c = elliptic.P256()
case ECCModeSecp384r1:
c = elliptic.P384()
case ECCModeSecp521r1:
c = elliptic.P521()
case ECCModeSecp256k1:
// TODO FIXME - unsupported?
return ECKeyPair{}, errors.New("unsupported ec key pair mode")
default:
return ECKeyPair{}, fmt.Errorf("invalid ec key pair mode %d", mode)
}

privateKey, err := ecdsa.GenerateKey(c, rand.Reader)
if err != nil {
return ECKeyPair{}, fmt.Errorf("ec.GenerateKey failed: %w", err)
}

ecKeyPair := ECKeyPair{PrivateKey: privateKey}
return ecKeyPair, nil
}

// PrivateKeyInPemFormat Returns private key in pem format.
func (keyPair ECKeyPair) PrivateKeyInPemFormat() (string, error) {
if keyPair.PrivateKey == nil {
return "", errors.New("failed to generate PEM formatted private key")
}

privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(keyPair.PrivateKey)
if err != nil {
return "", fmt.Errorf("x509.MarshalPKCS8PrivateKey failed: %w", err)
}

privateKeyPem := pem.EncodeToMemory(
&pem.Block{
Type: "PRIVATE KEY",
Bytes: privateKeyBytes,
},
)
return string(privateKeyPem), nil
}

// PublicKeyInPemFormat Returns public key in pem format.
func (keyPair ECKeyPair) PublicKeyInPemFormat() (string, error) {
if keyPair.PrivateKey == nil {
return "", errors.New("failed to generate PEM formatted public key")
}

publicKeyBytes, err := x509.MarshalPKIXPublicKey(&keyPair.PrivateKey.PublicKey)
if err != nil {
return "", fmt.Errorf("x509.MarshalPKIXPublicKey failed: %w", err)
}

publicKeyPem := pem.EncodeToMemory(
&pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
},
)

return string(publicKeyPem), nil
}

// KeySize Return the size of this ec key pair.
func (keyPair ECKeyPair) KeySize() (int, error) {
if keyPair.PrivateKey == nil {
return -1, errors.New("failed to return key size")
}
return keyPair.PrivateKey.Params().N.BitLen(), nil
}
74 changes: 74 additions & 0 deletions lib/ocrypto/ec_key_pair_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package ocrypto

import (
"testing"
)

func TestECKeyPair(t *testing.T) {
for _, modeGood := range []ECCMode{ECCModeSecp256r1, ECCModeSecp384r1, ECCModeSecp521r1} {
ecKeyPair, err := NewECKeyPair(modeGood)
if err != nil {
t.Fatalf("NewECKeyPair(%d): %v", modeGood, err)
}

_, err = ecKeyPair.PublicKeyInPemFormat()
if err != nil {
t.Fatalf("ec PublicKeyInPemFormat() error - %v", err)
}

_, err = ecKeyPair.PrivateKeyInPemFormat()
if err != nil {
t.Fatalf("ec PrivateKeyInPemFormat() error - %v", err)
}

keySize, err := ecKeyPair.KeySize()
if err != nil {
t.Fatalf("ec keysize error - %v", err)
}

// Set expected size based on mode
var size int
switch modeGood {
case ECCModeSecp256r1:
size = 256
case ECCModeSecp384r1:
size = 384
case ECCModeSecp521r1:
size = 521
case ECCModeSecp256k1:
fallthrough
default:
size = 99999 // deliberately bad value
}

if keySize != size {
t.Fatalf("invalid key size for mode %d, expected:%d actual:%d",
modeGood, size, keySize)
}
}

// Fail case
emptyECKeyPair := ECKeyPair{}

_, err := emptyECKeyPair.PrivateKeyInPemFormat()
if err == nil {
t.Fatal("EcKeyPair.PrivateKeyInPemFormat() fail to return error")
}

_, err = emptyECKeyPair.PublicKeyInPemFormat()
if err == nil {
t.Fatal("EcKeyPair.PublicKeyInPemFormat() fail to return error")
}

_, err = emptyECKeyPair.KeySize()
if err == nil {
t.Fatal("EcKeyPair.keySize() fail to return error")
}

for _, modeBad := range []ECCMode{ECCModeSecp256k1} {
_, err := NewECKeyPair(modeBad)
if err == nil {
t.Fatalf("did not fail as expected: NewECKeyPair(%d): %v", modeBad, err)
}
}
}
2 changes: 2 additions & 0 deletions sdk/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,5 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311173647-c811ad7063a7 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/opentdf/platform/ocrypto => ../lib/ocrypto