diff --git a/pkg/jwt/BUILD.bazel b/pkg/jwt/BUILD.bazel index 015347bb..6742dcc2 100644 --- a/pkg/jwt/BUILD.bazel +++ b/pkg/jwt/BUILD.bazel @@ -8,6 +8,7 @@ go_library( "demultiplexing_signature_validator.go", "ecdsa_sha_signature_generator.go", "ecdsa_sha_signature_validator.go", + "ed25519_signature_generator.go", "ed25519_signature_validator.go", "forwarding_signature_validator.go", "generate_authorization_header.go", @@ -40,6 +41,7 @@ go_test( "authorization_header_parser_test.go", "ecdsa_sha_signature_generator_test.go", "ecdsa_sha_signature_validator_test.go", + "ed25519_signature_generator_test.go", "ed25519_signature_validator_test.go", "generate_authorization_header_test.go", "hmac_sha_signature_validator_test.go", diff --git a/pkg/jwt/ed25519_signature_generator.go b/pkg/jwt/ed25519_signature_generator.go new file mode 100644 index 00000000..28184bef --- /dev/null +++ b/pkg/jwt/ed25519_signature_generator.go @@ -0,0 +1,31 @@ +package jwt + +import ( + "crypto/ed25519" +) + +type ed25519SignatureGenerator struct { + privateKey ed25519.PrivateKey +} + +// NewEd25519SignatureGenerator creates a SignatureGenerator that can +// sign a JWT using the Edwards-curve Digital Signature Algorithm +// (EdDSA), using Curve25519 as its elliptic curve and SHA-512 as a +// hashing algorithm. +// +// EdDSA uses asymmetrical cryptography, meaning that signing is +// performed using a private key, while verification only relies on a +// public key. +func NewEd25519SignatureGenerator(privateKey ed25519.PrivateKey) SignatureGenerator { + return ed25519SignatureGenerator{ + privateKey: privateKey, + } +} + +func (sc ed25519SignatureGenerator) GetAlgorithm() string { + return "EdDSA" +} + +func (sc ed25519SignatureGenerator) GenerateSignature(headerAndPayload string) ([]byte, error) { + return ed25519.Sign(sc.privateKey, []byte(headerAndPayload)), nil +} diff --git a/pkg/jwt/ed25519_signature_generator_test.go b/pkg/jwt/ed25519_signature_generator_test.go new file mode 100644 index 00000000..1b9fb031 --- /dev/null +++ b/pkg/jwt/ed25519_signature_generator_test.go @@ -0,0 +1,34 @@ +package jwt_test + +import ( + "crypto/ed25519" + "crypto/x509" + "encoding/pem" + "testing" + + "github.com/buildbarn/bb-storage/pkg/jwt" + "github.com/stretchr/testify/require" +) + +func TestEd25519SignatureGenerator(t *testing.T) { + // Create a signature generator. + block, _ := pem.Decode([]byte(`-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIJFl+ugysbBc60+O+IIFLSJL0TYtV1iW9W9YQ9t2l4MN +-----END PRIVATE KEY-----`)) + require.NotNil(t, block) + key, err := x509.ParsePKCS8PrivateKey(block.Bytes) + require.NoError(t, err) + privateKey := key.(ed25519.PrivateKey) + signatureGenerator := jwt.NewEd25519SignatureGenerator(privateKey) + require.Equal(t, "EdDSA", signatureGenerator.GetAlgorithm()) + + // Sign a token. + headerAndPayload := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ" + signature, err := signatureGenerator.GenerateSignature(headerAndPayload) + require.NoError(t, err) + + // Ensure that the generated signature is valid. + signatureValidator := jwt.NewEd25519SignatureValidator(privateKey.Public().(ed25519.PublicKey)) + require.NoError(t, err) + require.True(t, signatureValidator.ValidateSignature("EdDSA", nil, headerAndPayload, signature)) +}