Compare commits

...

5 Commits

Author SHA1 Message Date
Jan Dittberner 8c99fe2fab Implement handling of returned certificates 4 months ago
Jan Dittberner 73aad9d74e Continue certificate signing implementation
- parse PEM CSRs to DER before sending them to the signer
- handle SignCertificate and SignOpenPGP response messages
- add file system prefix constants for writing certificate files
- define supported hash algorithms
- fix handling of signing requests without organization, organizational unit,
  or location information
- use a different SMTP port for debugging with mailpit
4 months ago
Jan Dittberner a6317c82c5 Add legacydb package to support existing MySQL DB
- add new legacydb package
- fix warnings
4 months ago
Jan Dittberner eb92755ef6 Drop copyright year requirement 4 months ago
Jan Dittberner 8f0cf1a523 Update dependencies 4 months ago

@ -17,7 +17,7 @@ linters-settings:
const:
ORGANIZATION: CAcert Inc.
template: |-
Copyright {{ YEAR-RANGE }} {{ ORGANIZATION }}
Copyright {{ ORGANIZATION }}
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -31,6 +31,10 @@ import (
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
"git.cacert.org/cacert-gosigner/pkg/protocol"
"git.cacert.org/cacert-gosignerclient/internal/legacydb"
"git.cacert.org/cacert-gosignerclient/internal/client"
"git.cacert.org/cacert-gosignerclient/internal/config"
"git.cacert.org/cacert-gosignerclient/internal/handler"
@ -103,6 +107,8 @@ func generateDefaultConfig() error {
serial:
device: /dev/ttyUSB0
baud: 112500
database:
dsn: "user:password@/dbname"
`
cfg, err := config.LoadConfiguration(strings.NewReader(defaultBaseConfiguration))
@ -141,6 +147,18 @@ func startClient(configFile string, logger *logrus.Logger) error {
defer func() { _ = signerClient.Close() }()
commands := make(chan *protocol.Command, clientConfig.CommandChannelCapacity)
legacyDB, err := legacydb.New(logger, &clientConfig.Database, commands)
if err != nil {
return fmt.Errorf("could not initialize legacy database: %w", err)
}
defer func() { _ = legacyDB.Close() }()
signerClient.RegisterCommandSource(legacyDB)
signerClient.RegisterResponseSink(legacyDB)
logger.Info("setup complete, starting client operation")
ctx, cancel := context.WithCancel(context.Background())
@ -157,14 +175,14 @@ func startClient(configFile string, logger *logrus.Logger) error {
cancel()
}()
callbacks := make(chan interface{}, client.CallBackBufferSize)
callbacks := make(chan any, client.CallBackBufferSize)
clientHandler, err := handler.New(clientConfig, logger, callbacks)
if err != nil {
return fmt.Errorf("could not setup client handler: %w", err)
}
if err = signerClient.Run(ctx, callbacks, clientHandler); err != nil {
if err = signerClient.Run(ctx, callbacks, clientHandler, commands); err != nil {
return fmt.Errorf("error in client: %w", err)
}

@ -1,21 +1,27 @@
module git.cacert.org/cacert-gosignerclient
go 1.19
go 1.21
require (
git.cacert.org/cacert-gosigner v0.0.0-20221204124751-7852c4d3df8c
git.cacert.org/cacert-gosigner v0.0.0-20230917073743-2a800ecaf737
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c
github.com/balacode/go-delta v0.1.0
github.com/go-sql-driver/mysql v1.7.1
github.com/shamaton/msgpackgen v0.3.0
github.com/sirupsen/logrus v1.9.0
github.com/sirupsen/logrus v1.9.3
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07
github.com/wneessen/go-mail v0.4.0
golang.org/x/text v0.14.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/balacode/zr v1.1.2 // indirect
github.com/dave/jennifer v1.6.0 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/dave/jennifer v1.7.0 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/justincpresley/go-cobs v1.3.1 // indirect
github.com/shamaton/msgpack/v2 v2.1.1 // indirect
golang.org/x/sys v0.2.0 // indirect
golang.org/x/crypto v0.18.0 // indirect
golang.org/x/sys v0.16.0 // indirect
)

@ -1,18 +1,38 @@
git.cacert.org/cacert-gosigner v0.0.0-20221204124751-7852c4d3df8c h1:Awd3z2rKzBHiSuY/hpUBTr2Vnm08V8XxNibUkqaa0Ow=
git.cacert.org/cacert-gosigner v0.0.0-20221204124751-7852c4d3df8c/go.mod h1:OGIB5wLUhJiBhTzSXReOhGxuy7sT5VvyOyT8Ux8EGyw=
git.cacert.org/cacert-gosigner v0.0.0-20230917073743-2a800ecaf737 h1:P5YAs+zFMTPMuD6R0y7qB+9BmGnOKLw/NxdNPhSpUNU=
git.cacert.org/cacert-gosigner v0.0.0-20230917073743-2a800ecaf737/go.mod h1:I9TZphu6XcMrJEtboZ+6PR/9bsRcRBu4ND578Z1Aofk=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4 h1:ra2OtmuW0AE5csawV4YXMNGNQQXvLRps3z2Z59OPO+I=
github.com/ProtonMail/go-crypto v0.0.0-20221026131551-cf6655e29de4/go.mod h1:UBYPn8k0D56RtnR8RFQMjmh4KrZzWJ5o7Z9SYjossQ8=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE=
github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/balacode/go-delta v0.1.0 h1:pwz4CMn06P2bIaIfAx3GSabMPwJp/Ww4if+7SgPYa3I=
github.com/balacode/go-delta v0.1.0/go.mod h1:wLNrwTI3lHbPBvnLzqbHmA7HVVlm1u22XLvhbeA6t3o=
github.com/balacode/zr v1.0.0/go.mod h1:pLeSAL3DhZ9L0JuiRkUtIX3mLOCtzBLnDhfmykbSmkE=
github.com/balacode/zr v1.1.2 h1:DtZYCKtSTw8Giw/uv8zwkr2wb+C0Y7tx+947M077Ly0=
github.com/balacode/zr v1.1.2/go.mod h1:hN/XmTlDUdBTg3nd6sfc65aCrdKpVd1Y4sTYInF+T+U=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA=
github.com/dave/jennifer v1.6.0 h1:MQ/6emI2xM7wt0tJzJzyUik2Q3Tcn2eE0vtYgh4GPVI=
github.com/dave/jennifer v1.6.0/go.mod h1:AxTG893FiZKqxy3FP1kL80VMshSMuz2G+EgvszgGRnk=
github.com/dave/jennifer v1.7.0 h1:uRbSBH9UTS64yXbh4FrMHfgfY762RD+C7bUPKODpSJE=
github.com/dave/jennifer v1.7.0/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc=
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/justincpresley/go-cobs v1.3.1 h1:PbQ6FqJ5GTIECZz+FwgDTJlHl2avdboWpGrjJG9Khjg=
github.com/justincpresley/go-cobs v1.3.1/go.mod h1:L0d+EbGirv6IzsXNzwULduI2/z3ijkkAmsAuPMpLfqA=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -24,6 +44,8 @@ github.com/shamaton/msgpackgen v0.3.0 h1:q6o7prOEJFdF9BAPgkOtfzJbs55pQi7g44RUnEV
github.com/shamaton/msgpackgen v0.3.0/go.mod h1:fd99fDDuxuTiWzkHC59uEGzrt/WDu+ltGZTbEWwVXIc=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@ -32,9 +54,62 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07 h1:UyzmZLoiDWMRywV4DUYb9Fbt8uiOSooupjTq10vpvnU=
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
github.com/wneessen/go-mail v0.3.6 h1:hT8PMIBdcTkoiDwoUGJssPYOe1Gg1/cUcp2o9+ls63o=
github.com/wneessen/go-mail v0.3.6/go.mod h1:m25lkU2GYQnlVr6tdwK533/UXxo57V0kLOjaFYmub0E=
github.com/wneessen/go-mail v0.4.0 h1:Oo4HLIV8My7G9JuZkoOX6eipXQD+ACvIqURYeIzUc88=
github.com/wneessen/go-mail v0.4.0/go.mod h1:zxOlafWCP/r6FEhAaRgH4IC1vg2YXxO0Nar9u0IScZ8=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -82,21 +82,24 @@ type Client struct {
config *config.ClientConfig
signerInfo *SignerInfo
knownCACertificates map[string]*CACertificateInfo
commandSources []CommandSource
responseSinks map[messages.ResponseCode]ResponseSink
sync.Mutex
}
func (c *Client) Run(
ctx context.Context, callback <-chan interface{}, handler protocol.ClientHandler,
ctx context.Context, callback <-chan any, handler protocol.ClientHandler,
commands chan *protocol.Command,
) error {
const componentCount = 4
protocolErrors, framerErrors := make(chan error), make(chan error)
protocolErrors, framerErrors, sourceErrors := make(chan error), make(chan error), make(chan error)
subCtx, cancel := context.WithCancel(ctx)
wg := sync.WaitGroup{}
wg.Add(componentCount)
wg.Add(len(c.commandSources))
commands := make(chan *protocol.Command, c.config.CommandChannelCapacity)
fromSigner := make(chan []byte)
toSigner := make(chan []byte)
@ -107,6 +110,8 @@ func (c *Client) Run(
c.logger.Info("shutdown complete")
}()
c.RunSources(subCtx, &wg, sourceErrors)
go func(f protocol.Framer) {
defer wg.Done()
@ -171,6 +176,12 @@ func (c *Client) Run(
return fmt.Errorf("error from protocol: %w", err)
}
return nil
case err := <-sourceErrors:
if err != nil {
return fmt.Errorf("error from command source: %w", err)
}
return nil
}
}
@ -205,10 +216,15 @@ func (c *Client) Close() error {
type commandGenerator func(context.Context, chan<- *protocol.Command) error
func (c *Client) commandLoop(ctx context.Context, commands chan *protocol.Command, callback <-chan interface{}) {
func (c *Client) commandLoop(ctx context.Context, commands chan *protocol.Command, callback <-chan any) {
healthTimer := time.NewTimer(c.config.HealthStart)
fetchCRLTimer := time.NewTimer(c.config.FetchCRLStart)
nextCommands := make(chan *protocol.Command)
defer func() {
close(commands)
c.logger.Info("command loop stopped")
}()
for {
select {
@ -216,57 +232,54 @@ func (c *Client) commandLoop(ctx context.Context, commands chan *protocol.Comman
return
case callbackData := <-callback:
go func() {
err := c.handleCallback(ctx, nextCommands, callbackData)
err := c.handleCallback(ctx, commands, callbackData)
if err != nil {
c.logger.WithError(err).Error("callback handling failed")
}
}()
case <-fetchCRLTimer.C:
go c.scheduleRequiredCRLFetches(ctx, nextCommands)
go c.scheduleRequiredCRLFetches(ctx, commands)
fetchCRLTimer.Reset(c.config.FetchCRLInterval)
case <-healthTimer.C:
go c.scheduleHealthCheck(ctx, nextCommands)
go c.scheduleHealthCheck(ctx, commands)
healthTimer.Reset(c.config.HealthInterval)
case nextCommand, ok := <-nextCommands:
if !ok {
return
}
commands <- nextCommand
c.logger.WithFields(map[string]interface{}{
"command": nextCommand.Announce,
"buffer length": len(commands),
}).Trace("sent command")
}
}
}
type ErrNoResponseSink struct {
msg string
}
func (e ErrNoResponseSink) Error() string {
return fmt.Sprintf("no response sink for %s response found", e.msg)
}
func (c *Client) handleCallback(
ctx context.Context,
newCommands chan<- *protocol.Command,
data interface{},
data any,
) error {
var handler commandGenerator
var (
handler commandGenerator
err error
)
switch d := data.(type) {
case SignerInfo:
handler = c.updateSignerInfo(d)
case *messages.CAInfoResponse:
handler = c.updateCAInformation(d)
case *messages.FetchCRLResponse:
handler = c.updateCRL(d)
case *protocol.Response:
handler, err = c.handleResponse(d)
if err != nil {
return err
}
default:
return fmt.Errorf("unknown callback data of type %T", data)
return fmt.Errorf("unknown callback data of type %T", d)
}
if err := handler(ctx, newCommands); err != nil {
return err
}
return nil
return handler(ctx, newCommands)
}
func (c *Client) updateSignerInfo(
@ -609,6 +622,94 @@ func (c *Client) setLastKnownCRL(caName string, number *big.Int) {
caInfo.LastKnownCRL = number
}
type CommandSource interface {
Run(context.Context) error
}
type ResponseSink interface {
SupportedResponses() []messages.ResponseCode
HandleResponse(context.Context, *messages.ResponseAnnounce, any) error
NotifyError(ctx context.Context, requestID, message string) error
}
func (c *Client) RegisterCommandSource(source CommandSource) {
c.commandSources = append(c.commandSources, source)
}
func (c *Client) RegisterResponseSink(sink ResponseSink) {
for _, code := range sink.SupportedResponses() {
c.responseSinks[code] = sink
}
}
func (c *Client) handleResponse(r *protocol.Response) (commandGenerator, error) {
var handler commandGenerator
switch payload := r.Response.(type) {
case *messages.CAInfoResponse:
handler = c.updateCAInformation(payload)
case *messages.FetchCRLResponse:
handler = c.updateCRL(payload)
case *messages.ErrorResponse:
handler = func(ctx context.Context, _ chan<- *protocol.Command) error {
for _, sink := range c.responseSinks {
if err := sink.NotifyError(ctx, r.Announce.ID, payload.Message); err != nil {
return fmt.Errorf("error from response sink: %w", err)
}
}
return nil
}
case *messages.SignCertificateResponse:
sink, ok := c.responseSinks[messages.RespSignCertificate]
if !ok {
return nil, ErrNoResponseSink{"sign certificate"}
}
handler = func(ctx context.Context, _ chan<- *protocol.Command) error {
if err := sink.HandleResponse(ctx, r.Announce, payload); err != nil {
return fmt.Errorf("error from response sink: %w", err)
}
return nil
}
case *messages.SignOpenPGPResponse:
sink, ok := c.responseSinks[messages.RespSignOpenPGP]
if !ok {
return nil, ErrNoResponseSink{"sign openpgp"}
}
handler = func(ctx context.Context, _ chan<- *protocol.Command) error {
if err := sink.HandleResponse(ctx, r.Announce, payload); err != nil {
return fmt.Errorf("error from response sink: %w", err)
}
return nil
}
default:
return nil, fmt.Errorf("unhandled response %s", payload)
}
return handler, nil
}
func (c *Client) RunSources(ctx context.Context, wg *sync.WaitGroup, errorChan chan error) {
for _, source := range c.commandSources {
go func(s CommandSource) {
defer wg.Done()
err := s.Run(ctx)
if err != nil {
c.logger.WithError(err).Error("command source failed")
errorChan <- err
}
c.logger.Info("command source stopped")
}(source)
}
}
func New(
cfg *config.ClientConfig,
logger *logrus.Logger,
@ -623,6 +724,8 @@ func New(
framer: cobsFramer,
config: cfg,
knownCACertificates: make(map[string]*CACertificateInfo),
responseSinks: make(map[messages.ResponseCode]ResponseSink),
commandSources: make([]CommandSource, 0),
}
err = client.setupConnection(&serial.Config{

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright 2022-2023 CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -36,6 +36,9 @@ const (
defaultResponseDataTimeout = 2 * time.Second
defaultFilesDirectory = "public"
defaultCommandChannelCapacity = 100
defaultDatabaseConnMaxLiveTime = 3 * time.Minute
defaultDatabaseMaxOpenConns = 10
defaultDatabaseMaxIdleConns = 10
)
type SettingsError struct {
@ -52,6 +55,13 @@ type Serial struct {
Timeout time.Duration `yaml:"timeout"`
}
type Database struct {
DSN string `yaml:"dsn"`
ConnMaxLiveTime time.Duration `yaml:"conn-max-live-time"`
MaxOpenConns int `yaml:"max-open-conns"`
MaxIdleConns int `yaml:"max-idle-conns"`
}
type ClientConfig struct {
Serial Serial `yaml:"serial"`
HealthInterval time.Duration `yaml:"health-interval"`
@ -63,6 +73,7 @@ type ClientConfig struct {
PublicCRLDirectory string `yaml:"public-crl-directory"`
PublicCertificateDirectory string `yaml:"public-certificate-directory"`
CommandChannelCapacity int `yaml:"command-channel-capacity"`
Database Database `yaml:"database"`
}
func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
@ -77,6 +88,7 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
PublicCRLDirectory string `yaml:"public-crl-directory"`
PublicCertificateDirectory string `yaml:"public-certificate-directory"`
CommandChannelCapacity int `yaml:"command-channel-capacity"`
Database Database `yaml:"database"`
}{}
err := n.Decode(&data)
@ -84,16 +96,8 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
return fmt.Errorf("could not decode YAML: %w", err)
}
if data.Serial.Device == "" {
return SettingsError{"you must specify a serial 'device'"}
}
if data.Serial.Baud == 0 {
data.Serial.Baud = 115200
}
if data.Serial.Timeout == 0 {
data.Serial.Timeout = defaultSerialTimeout
if err := checkSerialConfig(&data.Serial); err != nil {
return err
}
c.Serial = data.Serial
@ -152,6 +156,50 @@ func (c *ClientConfig) UnmarshalYAML(n *yaml.Node) error {
c.PublicCertificateDirectory = data.PublicCRLDirectory
if err := checkDatabaseConfig(&data.Database); err != nil {
return err
}
c.Database = data.Database
c.Database = data.Database
return nil
}
func checkDatabaseConfig(d *Database) error {
if d.DSN == "" {
return SettingsError{"you must specify a database 'dsn'"}
}
if d.ConnMaxLiveTime == 0 {
d.ConnMaxLiveTime = defaultDatabaseConnMaxLiveTime
}
if d.MaxOpenConns == 0 {
d.MaxOpenConns = defaultDatabaseMaxOpenConns
}
if d.MaxIdleConns == 0 {
d.MaxIdleConns = defaultDatabaseMaxIdleConns
}
return nil
}
func checkSerialConfig(s *Serial) error {
if s.Device == "" {
return SettingsError{"you must specify a serial 'device'"}
}
if s.Baud == 0 {
s.Baud = 115200
}
if s.Timeout == 0 {
s.Timeout = defaultSerialTimeout
}
return nil
}

@ -1,5 +1,5 @@
/*
Copyright 2022 CAcert Inc.
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
Licensed under the Apache License, Version 2.0 (the "License");
@ -37,7 +37,7 @@ import (
type SignerClientHandler struct {
logger *logrus.Logger
config *config.ClientConfig
clientCallback chan<- interface{}
clientCallback chan<- any
}
var errInputClosed = errors.New("input channel has been closed")
@ -132,6 +132,20 @@ func (s *SignerClientHandler) ResponseData(ctx context.Context, in <-chan []byte
return fmt.Errorf("could not unmarshal fetch CRL response data: %w", err)
}
response.Response = &resp
case messages.RespSignCertificate:
var resp messages.SignCertificateResponse
if err := msgpack.Unmarshal(frame, &resp); err != nil {
return fmt.Errorf("could not unmarshal sign certificate response data: %w", err)
}
response.Response = &resp
case messages.RespSignOpenPGP:
var resp messages.SignOpenPGPResponse
if err := msgpack.Unmarshal(frame, &resp); err != nil {
return fmt.Errorf("could not unmarshal sign OpenPGP response data: %w", err)
}
response.Response = &resp
case messages.RespError:
var resp messages.ErrorResponse
@ -155,16 +169,13 @@ func (s *SignerClientHandler) HandleResponse(ctx context.Context, response *prot
s.logger.WithField("response", response).Debug("full response")
switch r := response.Response.(type) {
case *messages.ErrorResponse:
s.logger.WithField("message", r.Message).Error("error from signer")
case *messages.HealthResponse:
s.handleHealthResponse(ctx, r)
case *messages.CAInfoResponse:
s.handleCAInfoResponse(ctx, r)
case *messages.FetchCRLResponse:
s.handleFetchCRLResponse(ctx, r)
default:
s.logger.WithField("response", response).Warnf("unhandled response of type %T", response.Response)
s.logger.WithField("response", response).Tracef(
"delegate response handling of type %T", response.Response,
)
s.handleGenericResponse(ctx, response)
}
return nil
@ -215,20 +226,11 @@ func (s *SignerClientHandler) handleHealthResponse(ctx context.Context, r *messa
}
}
func (s *SignerClientHandler) handleCAInfoResponse(ctx context.Context, r *messages.CAInfoResponse) {
select {
case <-ctx.Done():
return
case s.clientCallback <- r:
break
}
}
func (s *SignerClientHandler) handleFetchCRLResponse(ctx context.Context, r *messages.FetchCRLResponse) {
func (s *SignerClientHandler) handleGenericResponse(ctx context.Context, response *protocol.Response) {
select {
case <-ctx.Done():
return
case s.clientCallback <- r:
case s.clientCallback <- response:
break
}
}
@ -236,7 +238,7 @@ func (s *SignerClientHandler) handleFetchCRLResponse(ctx context.Context, r *mes
func New(
config *config.ClientConfig,
logger *logrus.Logger,
clientCallback chan interface{},
clientCallback chan any,
) (protocol.ClientHandler, error) {
return &SignerClientHandler{
logger: logger,

File diff suppressed because it is too large Load Diff

@ -0,0 +1,186 @@
/*
Copyright CAcert Inc.
SPDX-License-Identifier: Apache-2.0
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 legacydb
import (
"crypto/x509"
"crypto/x509/pkix"
"testing"
"github.com/stretchr/testify/assert"
)
func Test_extractSubjectParts(t *testing.T) {
tests := []struct {
name string
subject string
want *x509.Certificate
wantErr bool
}{
{
"personal user subject",
"/CN=John Doe/emailAddress=john.doe@example.org",
&x509.Certificate{
Subject: pkix.Name{CommonName: "John Doe"},
EmailAddresses: []string{"john.doe@example.org"},
},
false,
},
{
"subject with supported and unsupported alt names",
"/CN=a.example.com/subjectAltName=DNS:a.example.com/" +
"subjectAltName=otherName:1.3.6.1.5.5.7.8.5;UTF8:a.example.com",
&x509.Certificate{
Subject: pkix.Name{CommonName: "a.example.com"},
DNSNames: []string{"a.example.com"},
},
false,
},
{
"subject with ISO-8859-1 special characters",
"/CN=D\xf6ner Kebap/emailAddress=doener@example.org",
&x509.Certificate{
Subject: pkix.Name{CommonName: "Döner Kebap"},
EmailAddresses: []string{"doener@example.org"},
},
false,
},
{
"subject with Windows1252 special characters",
"/CN=J\xe1no\x9a Test\x9c/emailAddress=janos.testoe@example.org",
&x509.Certificate{
Subject: pkix.Name{CommonName: "Jánoš Testœ"},
EmailAddresses: []string{"janos.testoe@example.org"},
},
false,
},
{
"WoT User subject",
"/CN=CAcert WoT User/emailAddress=test@example.org",
&x509.Certificate{
Subject: pkix.Name{CommonName: "CAcert WoT User"},
EmailAddresses: []string{"test@example.org"},
},
false,
},
{
"Keep address order",
"/CN=CAcert WoT User/emailAddress=wot.user@example.com/emailAddress=wu@example.com",
&x509.Certificate{
Subject: pkix.Name{CommonName: "CAcert WoT User"},
EmailAddresses: []string{"wot.user@example.com", "wu@example.com"},
},
false,
},
{
"Keep DNS name order",
"/CN=Test User/subjectAltName=DNS:www.example.com/subjectAltName=DNS:example.com",
&x509.Certificate{
Subject: pkix.Name{CommonName: "Test User"},
DNSNames: []string{"www.example.com", "example.com"},
},
false,
},
{
"Organization user without OU",
"/CN=Test User/emailAddress=test@example.org/organizationName=Acme Inc./" +
"localityName=Example town/stateOrProvinceName=BW/countryName=DE",
&x509.Certificate{
Subject: pkix.Name{
CommonName: "Test User",
Organization: []string{"Acme Inc."},
Locality: []string{"Example town"},
Province: []string{"BW"},
Country: []string{"DE"},
},
EmailAddresses: []string{"test@example.org"},
},
false,
},
{
"Organization user with OU",
"/CN=Test User/emailAddress=test@example.org/organizationalUnitName=IT/" +
"organizationName=Acme Inc./localityName=Example town/countryName=DE",
&x509.Certificate{
Subject: pkix.Name{
CommonName: "Test User",
Organization: []string{"Acme Inc."},
OrganizationalUnit: []string{"IT"},
Locality: []string{"Example town"},
Country: []string{"DE"},
},
EmailAddresses: []string{"test@example.org"},
},
false,
},
{
"Organization domain without OU",
"/organizationName=Acme Inc./localityName=Example Town/stateOrProvinceName=BW/countryName=DE/" +
"commonName=www.example.org",
&x509.Certificate{
Subject: pkix.Name{CommonName: "www.example.org",
Organization: []string{"Acme Inc."},
Locality: []string{"Example Town"},
Province: []string{"BW"},
Country: []string{"DE"},
},
DNSNames: []string{"www.example.org"},
},
false,
},
{
"Organization domain with OU",
"/organizationalUnitName=IT/organizationName=Acme Inc./localityName=Example Town/" +
"stateOrProvinceName=BW/countryName=DE/commonName=example.org",
&x509.Certificate{
Subject: pkix.Name{CommonName: "example.org",
Organization: []string{"Acme Inc."},
OrganizationalUnit: []string{"IT"},
Locality: []string{"Example Town"},
Province: []string{"BW"},
Country: []string{"DE"},
},
DNSNames: []string{"example.org"},
},
false,
},
{
"Empty subject",
"",
nil,
true,
},
{
"No = in part",
"/CNexample",
nil,
true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := extractSubjectParts(tt.subject)
if (err != nil) != tt.wantErr {
t.Errorf("extractSubjectParts() error = %v, wantErr %v", err, tt.wantErr)
return
}
assert.Equal(t, tt.want, got, "extractSubjectParts() got = %v, want %v", got, tt.want)
})
}
}

@ -0,0 +1,12 @@
Your CAcert signed key for {{ .Email }} is available online at:
https://www.cacert.org/gpg.php?id=3&cert={{ .RowID }}
To help improve the trust of CAcert in general, it's appreciated if you could also sign our key and upload it to a key
server. Below is a copy of our primary key details:
pub 1024D/65D0FD58 2003-07-11 CA Cert Signing Authority (Root CA) <gpg@cacert.org>
Key fingerprint = A31D 4F81 EF4E BD07 B456 FA04 D2BB 0D01 65D0 FD58
Best regards
CAcert.org Support!

@ -0,0 +1 @@
[CAcert.org] Your GPG/PGP Key
Loading…
Cancel
Save