Skip to content
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/controlplane/internal/biz/biz.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ var ProviderSet = wire.NewSet(
NewAPITokenUseCase,
NewAPITokenSyncerUseCase,
NewAttestationStateUseCase,
NewChainloopSigningUseCase,
wire.Struct(new(NewIntegrationUseCaseOpts), "*"),
wire.Struct(new(NewUserUseCaseParams), "*"),
)
Expand Down
106 changes: 106 additions & 0 deletions app/controlplane/internal/biz/signing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
//
// Copyright 2024 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package biz

import (
"context"
"crypto"
"crypto/x509"
"crypto/x509/pkix"
"errors"
"fmt"

"github.com/sigstore/fulcio/pkg/ca"
"github.com/sigstore/fulcio/pkg/identity"
"github.com/sigstore/sigstore/pkg/cryptoutils"
)

type SigningUseCase struct {
CA ca.CertificateAuthority
}

func NewChainloopSigningUseCase(ca ca.CertificateAuthority) *SigningUseCase {
return &SigningUseCase{CA: ca}
}

// CreateSigningCert signs a certificate request with a configured CA, and returns the full certificate chain
func (s *SigningUseCase) CreateSigningCert(ctx context.Context, orgID string, csrRaw []byte) ([]string, error) {
var publicKey crypto.PublicKey

if len(csrRaw) == 0 {
return nil, errors.New("csr cannot be empty")
}

// Parse CSR
csr, err := cryptoutils.ParseCSR(csrRaw)
if err != nil {
return nil, fmt.Errorf("parsing csr: %w", err)
}

// Parse public key and check for weak key parameters
publicKey = csr.PublicKey
if err := cryptoutils.ValidatePubKey(publicKey); err != nil {
return nil, fmt.Errorf("invalid public key: %w", err)
}

// Check the CSR signature is valid
if err := csr.CheckSignature(); err != nil {
return nil, fmt.Errorf("invalid signature: %w", err)
}

// Create certificate from CA provider (no Signed Certificate Timestamps for now)
csc, err := s.CA.CreateCertificate(ctx, newChainloopPrincipal(orgID), publicKey)
if err != nil {
return nil, fmt.Errorf("creating certificate: %w", err)
}

// Generated certificate
finalPEM, err := csc.CertPEM()
if err != nil {
return nil, fmt.Errorf("marshaling certificate to PEM: %w", err)
}

// Certificate chain
finalChainPEM, err := csc.ChainPEM()
if err != nil {
return nil, fmt.Errorf("marshaling chain to PEM: %w", err)
}

return append([]string{finalPEM}, finalChainPEM...), nil
}

type chainloopPrincipal struct {
orgID string
}

var _ identity.Principal = (*chainloopPrincipal)(nil)

func newChainloopPrincipal(orgID string) *chainloopPrincipal {
return &chainloopPrincipal{orgID: orgID}
}

func (p *chainloopPrincipal) Name(_ context.Context) string {
return p.orgID
}

func (p *chainloopPrincipal) Embed(_ context.Context, cert *x509.Certificate) error {
// no op.
// TODO: Chainloop might have their own private enterprise number with the Internet Assigned Numbers Authority
// to embed its own identity information in the resulting certificate
cert.Subject = pkix.Name{Organization: []string{p.orgID}}

return nil
}
94 changes: 94 additions & 0 deletions app/controlplane/internal/biz/signing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
//
// Copyright 2024 The Chainloop Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package biz_test

import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"testing"

"github.com/chainloop-dev/chainloop/app/controlplane/internal/biz"
"github.com/sigstore/fulcio/pkg/ca/ephemeralca"
"github.com/sigstore/sigstore/pkg/cryptoutils"
"github.com/stretchr/testify/suite"
)

type signingUseCaseTestSuite struct {
suite.Suite
uc *biz.SigningUseCase
csr []byte
}

func (s *signingUseCaseTestSuite) TestSigningUseCase_CreateSigningCert() {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: I think this is smth I asked @javirln once, but is using _ i.e TestSigningUseCase_CreateSigningCert go idiomatic?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the suggestion from the IDE, I believe it's some "idiomatic", but I don't think there is a written rule.

s.Run("with empty certificate", func() {
_, err := s.uc.CreateSigningCert(context.TODO(), "myorgid", make([]byte, 0))
s.Error(err)
})

s.Run("with certificate request", func() {
certChain, err := s.uc.CreateSigningCert(context.TODO(), "myorgid", s.csr)
s.NoError(err)

// assert 2 certificates: signing certificate + chain (only one)
s.Len(certChain, 2)

// check cert contents
cert, err := cryptoutils.UnmarshalCertificatesFromPEM([]byte(certChain[0]))
s.NoError(err)
s.Len(cert, 1)
s.Equal("myorgid", cert[0].Subject.Organization[0])
})
}

func TestSuite(t *testing.T) {
suite.Run(t, new(signingUseCaseTestSuite))
}

func (s *signingUseCaseTestSuite) SetupTest() {
csr, err := createCSR()
s.Require().NoError(err)
s.csr = csr

ca, err := ephemeralca.NewEphemeralCA()
s.Require().NoError(err)
s.uc = &biz.SigningUseCase{CA: ca}
}

func createCSR() ([]byte, error) {
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return nil, fmt.Errorf("generating cert: %w", err)
}
csrTmpl := &x509.CertificateRequest{Subject: pkix.Name{CommonName: "ephemeral certificate"}}
derCSR, err := x509.CreateCertificateRequest(rand.Reader, csrTmpl, priv)
if err != nil {
return nil, fmt.Errorf("generating certificate request: %w", err)
}

// Encode CSR to PEM
pemCSR := pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE REQUEST",
Bytes: derCSR,
})

return pemCSR, nil
}
8 changes: 7 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ require (
github.com/google/wire v0.6.0
github.com/googleapis/gax-go/v2 v2.12.3
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99
github.com/hashicorp/vault/api v1.12.2
github.com/hedwigz/entviz v0.0.0-20221011080911-9d47f6f1d818
github.com/improbable-eng/grpc-web v0.15.0
Expand Down Expand Up @@ -80,6 +80,7 @@ require (
github.com/openvex/go-vex v0.2.5
github.com/posthog/posthog-go v0.0.0-20240327112532-87b23fe11103
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/sigstore/fulcio v1.4.5
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.3
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.3
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.3
Expand Down Expand Up @@ -118,14 +119,17 @@ require (
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
github.com/go-playground/assert/v2 v2.2.0 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/goadesign/goa v2.2.5+incompatible // indirect
github.com/gobwas/ws v1.2.1 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/cel-go v0.20.1 // indirect
github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect
github.com/google/go-github/v55 v55.0.0 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/golang-lru v1.0.2 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
github.com/jackc/pgconn v1.14.3 // indirect
Expand Down Expand Up @@ -155,11 +159,13 @@ require (
github.com/sergi/go-diff v1.3.1 // indirect
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.2.0 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
go.opentelemetry.io/otel/metric v1.24.0 // indirect
goa.design/goa v2.2.5+incompatible // indirect
gopkg.in/go-jose/go-jose.v2 v2.6.3 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)
Expand Down
15 changes: 14 additions & 1 deletion go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,8 @@ github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/goadesign/goa v2.2.5+incompatible h1:SLgzk0V+QfFs7MVz9sbDHelbTDI9B/d4W7Hl5udTynY=
github.com/goadesign/goa v2.2.5+incompatible/go.mod h1:d/9lpuZBK7HFi/7O0oXfwvdoIl+nx2bwKqctZe/lQao=
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
Expand Down Expand Up @@ -732,11 +734,14 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDa
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk=
github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99 h1:JYghRBlGCZyCF2wNUJ8W0cwaQdtpcssJ4CgC406g+WU=
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.1-0.20210315223345-82c243799c99/go.mod h1:3bDW6wMZJB7tiONtC/1Xpicra6Wp5GgbTbQWCbI5fkc=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 h1:/c3QmbOGMGTOumP2iT/rCwB7b0QDGLKzqOmktBjT+Is=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1/go.mod h1:5SN9VR2LTsRFsrEC6FHgRbTWrTHu6tqPeKxEQv15giM=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE=
github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
Expand Down Expand Up @@ -787,6 +792,8 @@ github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
Expand Down Expand Up @@ -1172,6 +1179,7 @@ github.com/posthog/posthog-go v0.0.0-20240327112532-87b23fe11103/go.mod h1:Qjlpr
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
Expand All @@ -1189,6 +1197,7 @@ github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6T
github.com/prometheus/client_model v0.6.0 h1:k1v3CzpSRUTrKMppY35TLwPvxHqBu0bYgxZzqGIgaos=
github.com/prometheus/client_model v0.6.0/go.mod h1:NTQHnmxFpouOD0DpvP4XujX3CdOAGQPoaGhyTchlyt8=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
Expand All @@ -1199,6 +1208,7 @@ github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16
github.com/prometheus/common v0.51.1 h1:eIjN50Bwglz6a/c3hAgSMcofL3nD+nFQkV6Dd4DsQCw=
github.com/prometheus/common v0.51.1/go.mod h1:lrWtQx+iDfn2mbH5GUzlH9TSHyfZpHkSiG1W7y3sF2Q=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
Expand Down Expand Up @@ -1482,6 +1492,8 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
goa.design/goa v2.2.5+incompatible h1:mjAtiy7ZdZIkj974hpFxCR6bL69qprfV00Veu3Vybts=
goa.design/goa v2.2.5+incompatible/go.mod h1:NnzBwdNktihbNek+pPiFMQP9PPFsUt8MMPPyo9opDSo=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
Expand Down Expand Up @@ -1992,6 +2004,7 @@ google.golang.org/genproto/googleapis/bytestream v0.0.0-20240318140521-94a12d6c2
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.18.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
Expand Down