@ -50,7 +50,7 @@ type Profile struct {
UseFor string
}
type C ertInfo struct {
type C AC ertificate Info struct {
Name string
FetchCert bool
FetchCRL bool
@ -76,21 +76,18 @@ func (i *SignerInfo) containsCA(caName string) bool {
}
type Client struct {
port * serial . Port
logger * logrus . Logger
framer protocol . Framer
in chan [ ] byte
out chan [ ] byte
commands chan * protocol . Command
handler protocol . ClientHandler
config * config . ClientConfig
signerInfo * SignerInfo
knownCertificates map [ string ] * CertInfo
callback chan interface { }
port * serial . Port
logger * logrus . Logger
framer protocol . Framer
config * config . ClientConfig
signerInfo * SignerInfo
knownCACertificates map [ string ] * CACertificateInfo
sync . Mutex
}
func ( c * Client ) Run ( ctx context . Context ) error {
func ( c * Client ) Run (
ctx context . Context , callback <- chan interface { } , handler protocol . ClientHandler ,
) error {
const componentCount = 4
protocolErrors , framerErrors := make ( chan error ) , make ( chan error )
@ -99,6 +96,10 @@ func (c *Client) Run(ctx context.Context) error {
wg := sync . WaitGroup { }
wg . Add ( componentCount )
commands := make ( chan * protocol . Command , c . config . CommandChannelCapacity )
fromSigner := make ( chan [ ] byte )
toSigner := make ( chan [ ] byte )
defer func ( ) {
cancel ( )
c . logger . Info ( "context canceled, waiting for shutdown of components" )
@ -109,7 +110,7 @@ func (c *Client) Run(ctx context.Context) error {
go func ( f protocol . Framer ) {
defer wg . Done ( )
err := f . ReadFrames ( subCtx , c . port , c. in )
err := f . ReadFrames ( subCtx , c . port , fromSigner )
c . logger . Info ( "frame reading stopped" )
@ -122,7 +123,7 @@ func (c *Client) Run(ctx context.Context) error {
go func ( f protocol . Framer ) {
defer wg . Done ( )
err := f . WriteFrames ( subCtx , c . port , c. out )
err := f . WriteFrames ( subCtx , c . port , toSigner )
c . logger . Info ( "frame writing stopped" )
@ -135,7 +136,7 @@ func (c *Client) Run(ctx context.Context) error {
go func ( ) {
defer wg . Done ( )
clientProtocol := protocol . NewClient ( c. handler , c . commands , c . in , c . out , c . logger )
clientProtocol := protocol . NewClient ( handler, commands , fromSigner , toSigner , c . logger )
err := clientProtocol . Handle ( subCtx )
@ -150,7 +151,7 @@ func (c *Client) Run(ctx context.Context) error {
go func ( ) {
defer wg . Done ( )
c . commandLoop ( subCtx )
c . commandLoop ( subCtx , commands , callback )
c . logger . Info ( "client command loop stopped" )
} ( )
@ -192,9 +193,6 @@ func (c *Client) setupConnection(serialConfig *serial.Config) error {
}
func ( c * Client ) Close ( ) error {
close ( c . in )
close ( c . out )
if c . port != nil {
err := c . port . Close ( )
if err != nil {
@ -205,123 +203,150 @@ func (c *Client) Close() error {
return nil
}
func ( c * Client ) commandLoop ( ctx context . Context ) {
type commandGenerator func ( context . Context , chan <- * protocol . Command ) error
func ( c * Client ) commandLoop ( ctx context . Context , commands chan * protocol . Command , callback <- chan interface { } ) {
healthTimer := time . NewTimer ( c . config . HealthStart )
fetchCRLTimer := time . NewTimer ( c . config . FetchCRLStart )
nextCommands := make ( chan * protocol . Command )
for {
newCommands := make ( [ ] * protocol . Command , 0 )
select {
case <- ctx . Done ( ) :
return
case callbackData := <- c . c allback:
addCommands , err := c . handleCallback ( callbackData )
if err != nil {
c . logger . WithError ( err ) . Error ( "callback handling failed" )
}
newCommands = append ( newCommands , addCommands ... )
case callbackData := <- c allback:
go func ( ) {
err := c . handleCallback ( ctx , nextCommands , callbackData )
if err != nil {
c . logger . WithError ( err ) . Error ( "callback handling failed" )
}
} ( )
case <- fetchCRLTimer . C :
for _ , crlInfo := range c . requiredCRLs ( ) {
newCommands = append ( newCommands , command . FetchCRL ( crlInfo . Name , crlInfo . LastKnown ) )
}
go c . scheduleRequiredCRLFetches ( ctx , nextCommands )
fetchCRLTimer . Reset ( c . config . FetchCRLInterval )
case <- healthTimer . C :
newCommands = append ( newCommands , command . Health ( ) )
go c . scheduleHealthCheck ( ctx , nextCommands )
healthTimer . Reset ( c . config . HealthInterval )
}
for _ , nextCommand := range newCommands {
select {
case <- ctx . Done ( ) :
case nextCommand , ok := <- nextCommands :
if ! ok {
return
case c . commands <- nextCommand :
c . logger . WithField ( "command" , nextCommand . Announce ) . Trace ( "sent command" )
}
commands <- nextCommand
c . logger . WithFields ( map [ string ] interface { } {
"command" : nextCommand . Announce ,
"buffer length" : len ( commands ) ,
} ) . Trace ( "sent command" )
}
}
}
func ( c * Client ) handleCallback ( data interface { } ) ( [ ] * protocol . Command , error ) {
func ( c * Client ) handleCallback (
ctx context . Context ,
newCommands chan <- * protocol . Command ,
data interface { } ,
) error {
var handler commandGenerator
switch d := data . ( type ) {
case SignerInfo :
return c . updateSignerInfo ( d )
handler = c . updateSignerInfo ( d )
case * messages . CAInfoResponse :
return c . updateCAInformation ( d )
handler = c . updateCAInformation ( d )
case * messages . FetchCRLResponse :
return c . updateCRL ( d )
handler = c . updateCRL ( d )
default :
return nil , fmt . Errorf ( "unknown callback data of type %T" , data )
return fmt . Errorf ( "unknown callback data of type %T" , data )
}
if err := handler ( ctx , newCommands ) ; err != nil {
return err
}
return nil
}
func ( c * Client ) updateSignerInfo ( signerInfo SignerInfo ) ( [ ] * protocol . Command , error ) {
c . logger . Debug ( "update signer info" )
func ( c * Client ) updateSignerInfo (
signerInfo SignerInfo ,
) commandGenerator {
return func ( ctx context . Context , newCommands chan <- * protocol . Command ) error {
c . logger . Debug ( "update signer info" )
c . Lock ( )
c . signerInfo = & signerInfo
c . Unlock ( )
c . Lock ( )
c . signerInfo = & signerInfo
c . Unlock ( )
c . learnNewCACertificates ( )
c . learnNewCACertificates ( )
c . forgetRemovedCACertificates ( )
c . forgetRemovedCACertificates ( )
newCommands := make ( [ ] * protocol . Command , 0 )
for _ , caName := range c . requiredCertificateInfo ( ) {
select {
case <- ctx . Done ( ) :
case newCommands <- command . CAInfo ( caName ) :
}
}
for _ , caName := range c . requiredCertificateInfo ( ) {
newCommands = append ( newCommands , command . CAInfo ( caName ) )
return nil
}
return newCommands , nil
}
func ( c * Client ) updateCAInformation ( d * messages . CAInfoResponse ) ( [ ] * protocol . Command , error ) {
c . Lock ( )
defer c . Unlock ( )
func ( c * Client ) updateCAInformation (
infoResponse * messages . CAInfoResponse ,
) commandGenerator {
return func ( ctx context . Context , newCommands chan <- * protocol . Command ) error {
var (
caInfo * CACertificateInfo
cert * x509 . Certificate
err error
)
if caInfo , err = c . getCACertificate ( infoResponse . Name ) ; err != nil {
return err
}
caInfo , ok := c . knownCertificates [ d . Name ]
if ! ok {
c . logger . WithField ( "certificate" , d . Name ) . Warn ( "unknown CA certificate" )
if cert , err = x509 . ParseCertificate ( infoResponse . Certificate ) ; err != nil {
return fmt . Errorf ( "could not parse CA certificate for %s: %w" , infoResponse . Name , err )
}
return nil , nil
}
if ! cert . IsCA {
return fmt . Errorf ( "certificate for %s is not a CA certificate" , infoResponse . Name )
}
cert , err := x509 . ParseCertificate ( d . Certificate )
if err != nil {
return nil , fmt . Errorf ( "could not parse CA certificate for %s: %w" , d . Name , err )
}
if err = c . writeCertificate ( caInfo . Name , infoResponse . Certificate ) ; err != nil {
c . logger . WithError ( err ) . WithField ( "certificate" , infoResponse . Name ) . Warn (
"could not write CA certificate files" ,
)
}
if ! cert . IsCA {
return nil , fmt . Errorf ( "certificate for %s is not a CA certificate" , d . Name )
}
caInfo . Certificate = cert
caInfo . FetchCert = false
err = c . writeCertificate ( caInfo . Name , d . Certificate )
if err != nil {
c . logger . WithError ( err ) . WithField ( "certificate" , d . Name ) . Warn ( "could not write CA certificate files" )
}
caInfo . Profiles = make ( map [ string ] * Profile )
caInfo . Certificate = cert
caInfo . FetchCert = false
for _ , p := range infoResponse . Profiles {
caInfo . Profiles [ p . Name ] = & Profile {
Name : p . Name ,
UseFor : p . UseFor . String ( ) ,
}
}
caInfo . Profiles = make ( map [ string ] * Profile )
if len ( cert . CRLDistributionPoints ) == 0 {
caInfo . FetchCRL = false
for _ , p := range d . Profiles {
caInfo . Profiles [ p . Name ] = & Profile {
Name : p . Name ,
UseFor : p . UseFor . String ( ) ,
return nil
}
}
if len ( cert . CRLDistributionPoints ) == 0 {
caInfo . FetchCRL = false
select {
case <- ctx . Done ( ) :
case newCommands <- command . FetchCRL ( caInfo . Name , c . lastKnownCRL ( caInfo ) ) :
}
return nil , nil
return nil
}
return [ ] * protocol . Command { command . FetchCRL ( caInfo . Name , c . lastKnownCRL ( caInfo ) ) } , nil
}
type CRLInfo struct {
@ -329,40 +354,39 @@ type CRLInfo struct {
LastKnown * big . Int
}
func ( c * Client ) requiredCRLs ( ) [ ] CRLInfo {
c . Lock ( )
defer c . Unlock ( )
if c . knownCertificates == nil {
c . logger . Warn ( "no certificates known" )
return nil
}
func ( c * Client ) scheduleRequiredCRLFetches ( ctx context . Context , newCommands chan <- * protocol . Command ) {
infos := make ( [ ] CRLInfo , 0 )
for _ , caInfo := range c . knownCertificates {
c . Lock ( )
for _ , caInfo := range c . knownCACertificates {
if caInfo . FetchCRL {
infos = append ( infos , CRLInfo { Name : caInfo . Name , LastKnown : c . lastKnownCRL ( caInfo ) } )
}
}
c . Unlock ( )
return infos
for _ , crlInfo := range infos {
select {
case <- ctx . Done ( ) :
case newCommands <- command . FetchCRL ( crlInfo . Name , crlInfo . LastKnown ) :
}
}
}
func ( c * Client ) scheduleHealthCheck ( ctx context . Context , nextCommands chan <- * protocol . Command ) {
select {
case <- ctx . Done ( ) :
case nextCommands <- command . Health ( ) :
}
}
func ( c * Client ) requiredCertificateInfo ( ) [ ] string {
c . Lock ( )
defer c . Unlock ( )
if c . knownCertificates == nil {
c . logger . Warn ( "no certificates known" )
return nil
}
infos := make ( [ ] string , 0 )
for _ , caInfo := range c . knownC ertificates {
for _ , caInfo := range c . knownCACertificates {
if caInfo . FetchCert {
infos = append ( infos , caInfo . Name )
}
@ -371,7 +395,7 @@ func (c *Client) requiredCertificateInfo() []string {
return infos
}
func ( c * Client ) lastKnownCRL ( caInfo * C ertInfo) * big . Int {
func ( c * Client ) lastKnownCRL ( caInfo * C AC ertificate Info) * big . Int {
caName := caInfo . Name
crlFileName := c . buildCRLFileName ( caName )
@ -406,66 +430,62 @@ func (c *Client) lastKnownCRL(caInfo *CertInfo) *big.Int {
return lastKnown
}
func ( c * Client ) updateCRL ( d * messages . FetchCRLResponse ) ( [ ] * protocol . Command , error ) {
var (
crlNumber * big . Int
der [ ] byte
err error
)
caInfo , ok := c . knownCertificates [ d . IssuerID ]
if ! ok {
c . logger . WithField ( "certificate" , d . IssuerID ) . Warn ( "unknown CA certificate" )
}
func ( c * Client ) updateCRL ( fetchCRLResponse * messages . FetchCRLResponse ) commandGenerator {
return func ( _ context . Context , _ chan <- * protocol . Command ) error {
var (
crlNumber * big . Int
der [ ] byte
err error
list * x509 . RevocationList
)
if _ , err = c . getCACertificate ( fetchCRLResponse . IssuerID ) ; err != nil {
return err
}
if d . UnChanged {
c . logger . WithField ( "issuer" , d . IssuerID ) . Debug ( "CRL did not change" )
if fetchCRLResponse . UnChanged {
c . logger . WithField ( "issuer" , fetchCRLResponse . IssuerID ) . Debug ( "CRL did not change" )
return nil , nil
}
return nil
}
if ! d . IsDelta {
der = d . CRLData
if ! fetchCRLResponse . IsDelta {
der = fetchCRLResponse . CRLData
list , err := x509 . ParseRevocationList ( der )
if err != nil {
c . logger . WithError ( err ) . Error ( "CRL from signer could not be parsed" )
list , err = x509 . ParseRevocationList ( der )
if err != nil {
return fmt . Errorf (
"CRL for %s from signer could not be parsed: %w" ,
fetchCRLResponse . IssuerID ,
err ,
)
}
return nil , nil
}
crlNumber = list . Number
} else {
crlFileName := c . buildCRLFileName ( fetchCRLResponse . IssuerID )
crlNumber = list . Number
} else {
crlFileName := c . buildCRLFileName ( d . IssuerID )
if der , err = c . patchCRL ( crlFileName , fetchCRLResponse . CRLData ) ; err != nil {
return fmt . Errorf ( "CRL patching failed: %w" , err )
}
der , err = c . patchCRL ( crlFileName , d . CRLData )
if err != nil {
c . logger . WithError ( err ) . Error ( "CRL patching failed" )
if list , err = x509 . ParseRevocationList ( der ) ; err != nil {
return fmt . Errorf ( "could not parse patched CRL: %w" , err )
}
return nil , nil
crlNumber = list . Number
}
list , err := x509 . ParseRevocationList ( der )
if err != nil {
c . logger . WithError ( err ) . Error ( "could not parse patched CRL" )
if err = c . writeCRL ( fetchCRLResponse . IssuerID , der ) ; err != nil {
c . setLastKnownCRL ( fetchCRLResponse . IssuerID , nil )
return nil , nil
return fmt . Errorf ( "could not store CRL for %s: %w" , fetchCRLResponse . IssuerID , err )
}
crlNumber = list . Number
}
if err := c . writeCRL ( d . IssuerID , der ) ; err != nil {
c . logger . WithError ( err ) . Error ( "could not store CRL" )
caInfo . LastKnownCRL = nil
c . setLastKnownCRL ( fetchCRLResponse . IssuerID , crlNumber )
return nil , nil
return nil
}
caInfo . LastKnownCRL = crlNumber
return nil , nil
}
func ( c * Client ) buildCRLFileName ( caName string ) string {
@ -534,11 +554,11 @@ func (c *Client) learnNewCACertificates() {
defer c . Unlock ( )
for _ , caName := range c . signerInfo . CACertificates {
if _ , ok := c . knownC ertificates[ caName ] ; ok {
if _ , ok := c . knownC AC ertificates[ caName ] ; ok {
continue
}
c . knownC ertificates[ caName ] = & C ertInfo{
c . knownC AC ertificates[ caName ] = & C AC ertificate Info{
Name : caName ,
FetchCert : true ,
FetchCRL : true ,
@ -550,23 +570,48 @@ func (c *Client) forgetRemovedCACertificates() {
c . Lock ( )
defer c . Unlock ( )
for knownCA := range c . knownC ertificates {
for knownCA := range c . knownC AC ertificates {
if c . signerInfo . containsCA ( knownCA ) {
continue
}
c . logger . WithField ( "certificate" , knownCA ) . Warn ( "signer did not send status for certificate" )
delete ( c . knownC ertificates, knownCA )
delete ( c . knownC AC ertificates, knownCA )
}
}
func ( c * Client ) getCACertificate ( name string ) ( * CACertificateInfo , error ) {
c . Lock ( )
defer c . Unlock ( )
caInfo , ok := c . knownCACertificates [ name ]
if ! ok {
return nil , fmt . Errorf ( "no known CA certificate for %s" , name )
}
return caInfo , nil
}
func ( c * Client ) setLastKnownCRL ( caName string , number * big . Int ) {
c . Lock ( )
defer c . Unlock ( )
caInfo , ok := c . knownCACertificates [ caName ]
if ! ok {
c . logger . WithField ( "certificate" , caName ) . Warn (
"tried to set last known CRL for unknown CA certificate" ,
)
return
}
caInfo . LastKnownCRL = number
}
func New (
cfg * config . ClientConfig ,
logger * logrus . Logger ,
handler protocol . ClientHandler ,
commands chan * protocol . Command ,
callback chan interface { } ,
) ( * Client , error ) {
cobsFramer , err := protocol . NewCOBSFramer ( logger )
if err != nil {
@ -574,15 +619,10 @@ func New(
}
client := & Client {
logger : logger ,
framer : cobsFramer ,
in : make ( chan [ ] byte ) ,
out : make ( chan [ ] byte ) ,
commands : commands ,
handler : handler ,
config : cfg ,
callback : callback ,
knownCertificates : make ( map [ string ] * CertInfo ) ,
logger : logger ,
framer : cobsFramer ,
config : cfg ,
knownCACertificates : make ( map [ string ] * CACertificateInfo ) ,
}
err = client . setupConnection ( & serial . Config {