Start implementation of revoke action

This commit is contained in:
Jan Dittberner 2021-01-04 20:39:35 +01:00
parent 38566f35ef
commit 2de9771472
9 changed files with 739 additions and 57 deletions

View file

@ -2,7 +2,9 @@ package datastructures
import "encoding/binary" import "encoding/binary"
func encode24BitLength(data []byte) []byte { const signerTimeFormat = "010203042006.05"
func Encode24BitLength(data []byte) []byte {
lengthBytes := make([]byte, 4) lengthBytes := make([]byte, 4)
binary.BigEndian.PutUint32(lengthBytes, uint32(len(data))) binary.BigEndian.PutUint32(lengthBytes, uint32(len(data)))
return lengthBytes[1:] return lengthBytes[1:]

View file

@ -3,23 +3,24 @@ package datastructures
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt"
"time" "time"
"git.cacert.org/cacert-gosigner/shared" "git.cacert.org/cacert-gosigner/shared"
) )
type SignerRequest struct { type SignerRequest struct {
Version uint8 Version uint8
Action shared.Action Action shared.Action
System uint8 System shared.CryptoSystemId
Root uint8 Root shared.CryptoSystemRootId
Configuration uint8 Profile shared.CertificateProfileId
Parameter1 uint8 MdAlgorithm shared.MessageDigestAlgorithmId
Parameter2 uint16 Days uint16
Parameter3 uint8 Spkac uint8
Content1 string Content1 string
Content2 string Content2 string
Content3 string Content3 string
} }
func SignerRequestFromData(blockData []byte) (*SignerRequest, error) { func SignerRequestFromData(blockData []byte) (*SignerRequest, error) {
@ -39,42 +40,66 @@ func SignerRequestFromData(blockData []byte) (*SignerRequest, error) {
content3 := string(contentBytes[3+content3Offset : 3+content3Offset+content3Length]) content3 := string(contentBytes[3+content3Offset : 3+content3Offset+content3Length])
return &SignerRequest{ return &SignerRequest{
Version: headerBytes[0], Version: headerBytes[0],
Action: shared.Action(headerBytes[1]), Action: shared.Action(headerBytes[1]),
System: headerBytes[2], System: shared.CryptoSystemId(headerBytes[2]),
Root: headerBytes[3], Root: shared.CryptoSystemRootId(headerBytes[3]),
Configuration: headerBytes[4], Profile: shared.CertificateProfileId(headerBytes[4]),
Parameter1: headerBytes[5], MdAlgorithm: shared.MessageDigestAlgorithmId(headerBytes[5]),
Parameter2: binary.BigEndian.Uint16([]byte{headerBytes[6], headerBytes[7]}), Days: binary.BigEndian.Uint16([]byte{headerBytes[6], headerBytes[7]}),
Parameter3: headerBytes[8], Spkac: headerBytes[8],
Content1: content1, Content1: content1,
Content2: content2, Content2: content2,
Content3: content3, Content3: content3,
}, nil }, nil
} }
func (r SignerRequest) Serialize() []byte { func (r *SignerRequest) Serialize() []byte {
parameter2Bytes := make([]byte, 2) parameter2Bytes := make([]byte, 2)
binary.BigEndian.PutUint16(parameter2Bytes, r.Parameter2) binary.BigEndian.PutUint16(parameter2Bytes, r.Days)
headerBytes := bytes.Join([][]byte{ headerBytes := bytes.Join([][]byte{
{r.Version, byte(r.Action), r.System, r.Root, r.Configuration, r.Parameter1}, {
parameter2Bytes, {r.Parameter3}}, []byte{}) r.Version,
byte(r.Action),
byte(r.System),
byte(r.Root),
byte(r.Profile),
byte(r.MdAlgorithm),
},
parameter2Bytes, {r.Spkac}}, []byte{})
content1Bytes := []byte(r.Content1) content1Bytes := []byte(r.Content1)
content2Bytes := []byte(r.Content2) content2Bytes := []byte(r.Content2)
content3Bytes := []byte(r.Content3) content3Bytes := []byte(r.Content3)
blockBytes := bytes.Join([][]byte{ blockBytes := bytes.Join([][]byte{
encode24BitLength(headerBytes), headerBytes, Encode24BitLength(headerBytes), headerBytes,
encode24BitLength(content1Bytes), content1Bytes, Encode24BitLength(content1Bytes), content1Bytes,
encode24BitLength(content2Bytes), content2Bytes, Encode24BitLength(content2Bytes), content2Bytes,
encode24BitLength(content3Bytes), content3Bytes, Encode24BitLength(content3Bytes), content3Bytes,
}, []byte{}) }, []byte{})
return bytes.Join([][]byte{encode24BitLength(blockBytes), blockBytes}, []byte{}) return bytes.Join([][]byte{Encode24BitLength(blockBytes), blockBytes}, []byte{})
}
func (r *SignerRequest) String() string {
return fmt.Sprintf(
"v:%d %s s:%d r:%d p:%d md:%d days:%d spkac:%d '%s' '%s' '%s'",
r.Version,
r.Action,
r.System,
r.Root,
r.Profile,
r.MdAlgorithm,
r.Days,
r.Spkac,
r.Content1,
r.Content2,
r.Content3,
)
} }
func NewNulRequest() *SignerRequest { func NewNulRequest() *SignerRequest {
return &SignerRequest{ return &SignerRequest{
Version: shared.ProtocolVersion, Version: shared.ProtocolVersion,
Action: shared.ActionNul, Action: shared.ActionNul,
Content1: time.Now().UTC().Format("010203042006.05"), Content1: time.Now().UTC().Format(signerTimeFormat),
} }
} }

View file

@ -62,10 +62,20 @@ func (r SignerResponse) Serialize() []byte {
content2Bytes := []byte(r.Content2) content2Bytes := []byte(r.Content2)
content3Bytes := []byte(r.Content3) content3Bytes := []byte(r.Content3)
blockBytes := bytes.Join([][]byte{ blockBytes := bytes.Join([][]byte{
encode24BitLength(headerBytes), headerBytes, Encode24BitLength(headerBytes), headerBytes,
encode24BitLength(content1Bytes), content1Bytes, Encode24BitLength(content1Bytes), content1Bytes,
encode24BitLength(content2Bytes), content2Bytes, Encode24BitLength(content2Bytes), content2Bytes,
encode24BitLength(content3Bytes), content3Bytes, Encode24BitLength(content3Bytes), content3Bytes,
}, []byte{}) }, []byte{})
return bytes.Join([][]byte{encode24BitLength(blockBytes), blockBytes}, []byte{}) return bytes.Join([][]byte{Encode24BitLength(blockBytes), blockBytes}, []byte{})
}
func NewNulResponse(version byte) *SignerResponse {
return &SignerResponse{
Version: version,
Action: shared.ActionNul,
Content1: "",
Content2: "",
Content3: "",
}
} }

1
go.mod
View file

@ -5,5 +5,6 @@ go 1.15
require ( require (
github.com/sirupsen/logrus v1.7.0 github.com/sirupsen/logrus v1.7.0
go.bug.st/serial v1.1.1 go.bug.st/serial v1.1.1
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
) )

View file

@ -14,7 +14,7 @@ const (
TrailerFieldSize = len(MagicTrailer) TrailerFieldSize = len(MagicTrailer)
) )
type Action uint8 type Action byte
const ( const (
ActionNul = Action(0) ActionNul = Action(0)
@ -34,3 +34,11 @@ func (a Action) String() string {
return "unknown" return "unknown"
} }
} }
type CryptoSystemRootId byte
type CertificateProfileId byte
type MessageDigestAlgorithmId byte
type CryptoSystemId byte

View file

@ -1,8 +1,19 @@
package signer package signer
import ( import (
"bytes"
"crypto/rand"
"crypto/sha1"
"crypto/x509"
"crypto/x509/pkix"
"encoding/hex"
"encoding/pem"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"os"
"path"
"time"
"unsafe" "unsafe"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -12,28 +23,45 @@ import (
"git.cacert.org/cacert-gosigner/shared" "git.cacert.org/cacert-gosigner/shared"
) )
const crlLifetime = time.Hour * 24 * 7
var emptyDataSha1 [20]byte
func init() {
emptyDataSha1 = sha1.Sum([]byte{})
}
type CommandProcessorSettings struct {
CABaseDir string
XDeltaPath string
OpenPGPUidEmail string
}
// The CommandProcessor takes parsed protocol data and executes the actual // The CommandProcessor takes parsed protocol data and executes the actual
// functionality. // functionality.
type CommandProcessor struct { type CommandProcessor struct {
Settings *CommandProcessorSettings
CryptoSystems map[shared.CryptoSystemId]*CryptoSystem
} }
// Process the signer request // Process the signer request
func (p *CommandProcessor) Process(command datastructures.SignerRequest) ( func (p *CommandProcessor) Process(command *datastructures.SignerRequest) (
response *datastructures.SignerResponse, *datastructures.SignerResponse,
err error, error,
) { ) {
log.Infof("analyze %+v", command) log.Infof("process %s", command)
if command.Version != shared.ProtocolVersion {
return nil, fmt.Errorf("unsupported protocol version %d", command.Version)
}
switch command.Action { switch command.Action {
case shared.ActionNul: case shared.ActionNul:
response, err = p.handleNulAction(command) return p.handleNulAction(command)
return
case shared.ActionSign: case shared.ActionSign:
response, err = p.handleSignAction(command) return p.handleSignAction(command)
return
case shared.ActionRevoke: case shared.ActionRevoke:
response, err = p.handleRevokeAction(command) return p.handleRevokeAction(command)
return
default: default:
return nil, errors.New(fmt.Sprintf( return nil, errors.New(fmt.Sprintf(
"unsupported Action 0x%02x %s", "unsupported Action 0x%02x %s",
@ -43,7 +71,7 @@ func (p *CommandProcessor) Process(command datastructures.SignerRequest) (
} }
} }
func (*CommandProcessor) handleNulAction(command datastructures.SignerRequest) ( func (*CommandProcessor) handleNulAction(command *datastructures.SignerRequest) (
*datastructures.SignerResponse, *datastructures.SignerResponse,
error, error,
) { ) {
@ -64,13 +92,11 @@ func (*CommandProcessor) handleNulAction(command datastructures.SignerRequest) (
log.Errorf("could not set system time: %v", e1) log.Errorf("could not set system time: %v", e1)
} }
return &datastructures.SignerResponse{ return datastructures.NewNulResponse(command.Version), nil
Version: command.Version, Action: command.Action,
Content1: "", Content2: "", Content3: ""}, nil
} }
func (p *CommandProcessor) handleSignAction( func (p *CommandProcessor) handleSignAction(
command datastructures.SignerRequest, command *datastructures.SignerRequest,
) ( ) (
*datastructures.SignerResponse, *datastructures.SignerResponse,
error, error,
@ -80,11 +106,239 @@ func (p *CommandProcessor) handleSignAction(
} }
func (p *CommandProcessor) handleRevokeAction( func (p *CommandProcessor) handleRevokeAction(
command datastructures.SignerRequest, command *datastructures.SignerRequest,
) ( ) (
*datastructures.SignerResponse, *datastructures.SignerResponse,
error, error,
) { ) {
log.Debugf("handle revoke call: %v", command) log.Debugf("handle revoke call: %v", command)
idSystem, err := p.checkIdentitySystem(
command.System,
command.Root,
command.Profile,
command.MdAlgorithm,
)
if err != nil {
return nil, err
}
log.Debugf("identified id system: %+v", idSystem)
switch command.System {
case CsX509:
request := command.Content1
clientHash := command.Content3
return p.revokeX509(idSystem, request, clientHash)
default:
return nil, fmt.Errorf("revoke not implemented for crypto system %s", idSystem.System)
}
}
type IdSystemParameters struct {
System *CryptoSystem
Root *RootCredentials
RootId shared.CryptoSystemRootId
Profile string
MessageDigestAlgorithm x509.SignatureAlgorithm
}
func (p *CommandProcessor) checkIdentitySystem(
systemId shared.CryptoSystemId,
rootId shared.CryptoSystemRootId,
profileId shared.CertificateProfileId,
algorithmId shared.MessageDigestAlgorithmId,
) (*IdSystemParameters, error) {
cryptoSystem, ok := p.CryptoSystems[systemId]
if !ok {
return nil, fmt.Errorf(
"unsupported crypto system %d",
systemId,
)
}
root, ok := p.CryptoSystems[systemId].Roots[rootId]
if !ok {
return nil, fmt.Errorf(
"unsupported root certificate %d for crypto system %s",
rootId,
cryptoSystem,
)
}
profile, ok := p.CryptoSystems[systemId].Profiles[profileId]
if !ok {
return nil, fmt.Errorf(
"invalid CA profile %d for crypto system %s",
profileId,
cryptoSystem,
)
}
mdAlgorithm, ok := p.CryptoSystems[systemId].DigestAlgorithms[algorithmId]
if !ok {
return nil, fmt.Errorf(
"unsupported digest algorithm %d for crypto system %s",
algorithmId,
cryptoSystem,
)
}
return &IdSystemParameters{
System: cryptoSystem,
Root: root,
RootId: rootId,
Profile: profile,
MessageDigestAlgorithm: mdAlgorithm,
}, nil
}
func (p *CommandProcessor) revokeX509(
system *IdSystemParameters,
request string,
clientHash string,
) (*datastructures.SignerResponse, error) {
clientHashBytes, err := hex.DecodeString(clientHash)
if err != nil {
return nil, fmt.Errorf("could not parse '%s' as hex bytes", clientHash)
}
log.Debugf("revoke X.509 for root %s (%d)", system.Root, system.RootId)
log.Debugf("hash bytes from client %x", clientHashBytes)
crlFileName := fmt.Sprintf("revoke-root%d.crl", system.RootId)
stat, err := os.Stat(crlFileName)
if err != nil {
log.Debugf("CRL file %s does not exist", crlFileName)
} else if stat.IsDir() {
return nil, fmt.Errorf("CRL filename %s points to a directory", crlFileName)
}
currentHash := emptyDataSha1
if stat != nil {
crlData, err := ioutil.ReadFile(crlFileName)
if err != nil {
log.Warnf("could not read current CRL, assuming empty CRL")
}
currentHash = sha1.Sum(crlData)
}
log.Debugf("hash bytes on signer %x", currentHash)
crlIsCurrent := false
if bytes.Equal(clientHashBytes, currentHash[:]) {
log.Debug("client CRL hash and current CRL hash on signer match")
err := p.deleteOldCrl(system.RootId, clientHash)
if err != nil {
log.Warnf("could not delete old CRLs: %v", err)
}
if !bytes.Equal(clientHashBytes, emptyDataSha1[:]) {
crlIsCurrent = true
} else {
log.Debugf("client has an empty CRL, the signer too")
}
}
if len(request) > 0 {
_, err = revokeCertificate(request, system.System, system.RootId)
if err != nil {
return nil, fmt.Errorf("could not revoke certificate / create CRL: %v", err)
}
}
var crlBytes []byte
crlBytes, err = generateCrl(system.MessageDigestAlgorithm, system.System, system.RootId)
if err != nil {
return nil, fmt.Errorf("could not generate a new CRL for root %d: %v", system.RootId, err)
}
log.Debugf("crlIsCurrent: %v, crlBytes: %d", crlIsCurrent, len(crlBytes))
log.Tracef("New CRL\n%s", pem.EncodeToMemory(&pem.Block{
Type: "X509 CRL",
Bytes: crlBytes,
}))
err = ioutil.WriteFile(crlFileName, crlBytes, 0644)
if err != nil {
return nil, fmt.Errorf("could not write new CRL to %s: %v", crlFileName, err)
}
return nil, errors.New("not implemented yet") return nil, errors.New("not implemented yet")
} }
func (p *CommandProcessor) deleteOldCrl(id shared.CryptoSystemRootId, hash string) error {
path.Join(p.Settings.CABaseDir, "currentcrls", fmt.Sprintf("%d", id))
// TODO: implement
return errors.New("not implemented yet")
}
func generateCrl(
algorithm x509.SignatureAlgorithm,
system *CryptoSystem,
rootId shared.CryptoSystemRootId,
) ([]byte, error) {
caCertificate, err := system.loadCertificate(rootId)
if err != nil {
return nil, err
}
caPrivateKey, err := system.getPrivateKey(rootId)
if err != nil {
return nil, err
}
certificatesToRevoke, err := system.loadRevokedCertificatesFromDatabase(rootId)
if err != nil {
return nil, err
}
nextCrlNumber, err := system.getNextCRLNumber(rootId)
if err != nil {
return nil, err
}
crlTemplate := &x509.RevocationList{
SignatureAlgorithm: algorithm,
RevokedCertificates: certificatesToRevoke,
Number: nextCrlNumber,
ThisUpdate: time.Now(),
NextUpdate: time.Now().Add(crlLifetime),
ExtraExtensions: nil,
}
defer func() {
if err = system.bumpCRLNumber(rootId, nextCrlNumber); err != nil {
log.Errorf("could not bump CRL number: %v", err)
}
}()
return x509.CreateRevocationList(
rand.Reader,
crlTemplate,
caCertificate,
caPrivateKey,
)
}
func revokeCertificate(request string, system *CryptoSystem, rootId shared.CryptoSystemRootId) (*pkix.RevokedCertificate, error) {
pemBlock, _ := pem.Decode([]byte(request))
if pemBlock.Type != "CERTIFICATE" {
if pemBlock.Type != "CERTIFICATE" {
log.Warnf(
"PEM structure is probably not a certificate. PEM block has type %s",
pemBlock.Type,
)
log.Trace(request)
}
}
certificate, err := x509.ParseCertificate(pemBlock.Bytes)
if err != nil {
return nil, fmt.Errorf(
"could no parse certificate: %v",
err,
)
}
return system.recordRevocation(rootId, certificate)
}
func NewCommandProcessorSettings() *CommandProcessorSettings {
caBasedir, ok := os.LookupEnv("SIGNER_BASEDIR")
if !ok {
caBasedir = "."
}
gpgUidEmail, ok := os.LookupEnv("SIGNER_GPG_KEYRING_DIR")
if !ok {
gpgUidEmail = "gpg@cacert.org"
}
return &CommandProcessorSettings{CABaseDir: caBasedir, OpenPGPUidEmail: gpgUidEmail, XDeltaPath: "/usr/bin/xdelta"}
}

251
signer/crypto_system.go Normal file
View file

@ -0,0 +1,251 @@
package signer
import (
"bufio"
"crypto"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"io/ioutil"
"math/big"
"os"
"path"
"strconv"
"strings"
"time"
log "github.com/sirupsen/logrus"
"git.cacert.org/cacert-gosigner/shared"
)
type RootCredentials struct {
Name string
PrivateKeyFile string
PublicKeyFile string
CertificateFile string
DatabaseFile string
CRLNumber string
}
type CryptoSystem struct {
Name string
Roots map[shared.CryptoSystemRootId]*RootCredentials
Profiles map[shared.CertificateProfileId]string
DigestAlgorithms map[shared.MessageDigestAlgorithmId]x509.SignatureAlgorithm
}
func (system CryptoSystem) String() string {
return system.Name
}
func (system CryptoSystem) loadCertificate(rootId shared.CryptoSystemRootId) (*x509.Certificate, error) {
certificateFile := system.Roots[rootId].CertificateFile
pemBytes, err := ioutil.ReadFile(certificateFile)
if err != nil {
return nil, fmt.Errorf(
"could not load certificate %s: %v",
certificateFile,
err,
)
}
pemBlock, _ := pem.Decode(pemBytes)
if pemBlock.Type != "CERTIFICATE" {
log.Warnf(
"PEM in %s is probably not a certificate. PEM block has type %s",
certificateFile,
pemBlock.Type,
)
}
certificate, err := x509.ParseCertificate(pemBlock.Bytes)
if err != nil {
return nil, fmt.Errorf(
"could no parse certificate from %s: %v",
certificateFile,
err,
)
}
return certificate, nil
}
func (system CryptoSystem) getPrivateKey(rootId shared.CryptoSystemRootId) (crypto.Signer, error) {
privateKeyFile := system.Roots[rootId].PrivateKeyFile
pemBytes, err := ioutil.ReadFile(privateKeyFile)
if err != nil {
return nil, fmt.Errorf(
"could not load private key %s: %v",
privateKeyFile,
err,
)
}
pemBlock, _ := pem.Decode(pemBytes)
if pemBlock.Type != "PRIVATE KEY" {
log.Warnf(
"PEM in %s is probably not a private key. PEM block has type %s",
privateKeyFile,
pemBlock.Type,
)
}
privateKey, err := x509.ParsePKCS8PrivateKey(pemBlock.Bytes)
if err != nil {
return nil, fmt.Errorf(
"could no parse private key from %s: %v",
privateKeyFile,
err,
)
}
return privateKey.(*rsa.PrivateKey), nil
}
func (system *CryptoSystem) getNextCRLNumber(rootId shared.CryptoSystemRootId) (*big.Int, error) {
crlNumberFile := system.Roots[rootId].CRLNumber
_, err := os.Stat(crlNumberFile)
if err != nil {
log.Warnf("CRL number file %s does not exist: %v", crlNumberFile, err)
return big.NewInt(1), nil
}
data, err := ioutil.ReadFile(crlNumberFile)
if err != nil {
return nil, fmt.Errorf("could not read CRL number file %s", crlNumberFile)
}
result, err := stringAsBigInt(data)
if err != nil {
return nil, fmt.Errorf("could not parse content of %s as CRL number: %v", crlNumberFile, err)
}
return result, nil
}
func (system *CryptoSystem) bumpCRLNumber(rootId shared.CryptoSystemRootId, current *big.Int) error {
serial := current.Int64() + 1
crlNumberFile := system.Roots[rootId].CRLNumber
outFile, err := ioutil.TempFile(path.Dir(crlNumberFile), "*.txt")
defer func() { _ = outFile.Close() }()
_, err = outFile.WriteString(fmt.Sprintf(
"%s\n",
strings.ToUpper(strconv.FormatInt(serial, 16)),
))
if err != nil {
return fmt.Errorf("could not write new CRL number %d to %s: %v", serial, outFile.Name(), err)
}
if err = outFile.Close(); err != nil {
return fmt.Errorf("could not close temporary file %s: %v", outFile.Name(), err)
}
if err = os.Rename(crlNumberFile, fmt.Sprintf("%s.old", crlNumberFile)); err != nil {
return fmt.Errorf("could not rename %s to %s.old: %v", crlNumberFile, crlNumberFile, err)
}
if err = os.Rename(outFile.Name(), crlNumberFile); err != nil {
return fmt.Errorf("could not rename %s to %s: %v", outFile.Name(), crlNumberFile, err)
}
return nil
}
func stringAsBigInt(data []byte) (*big.Int, error) {
dataString := strings.TrimSpace(string(data))
parseInt, err := strconv.ParseInt(dataString, 16, 64)
if err != nil {
return nil, fmt.Errorf("could not parse %s as big int: %v", dataString, err)
}
return big.NewInt(parseInt), nil
}
func (system *CryptoSystem) loadRevokedCertificatesFromDatabase(rootId shared.CryptoSystemRootId) ([]pkix.RevokedCertificate, error) {
databaseFile := system.Roots[rootId].DatabaseFile
_, err := os.Stat(databaseFile)
if err != nil {
log.Warnf("openssl certificate database file %s does not exist: %v", databaseFile, err)
return []pkix.RevokedCertificate{}, nil
}
file, err := os.Open(databaseFile)
if err != nil {
return nil, fmt.Errorf("could not open openssl certificate database file %s: %v", databaseFile, err)
}
defer func() { _ = file.Close() }()
result := make([]pkix.RevokedCertificate, 0)
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.Split(scanner.Text(), "\t")
if line[0] == "R" {
serialNumber, err := stringAsBigInt([]byte(line[3]))
if err != nil {
return nil, fmt.Errorf("could not parse serial number %s as big int: %v", line[3], err)
}
revokeTs, err := strconv.ParseInt(line[2][:len(line[2])-1], 10, 64)
result = append(result, pkix.RevokedCertificate{
SerialNumber: serialNumber,
RevocationTime: time.Unix(revokeTs, 0),
Extensions: nil,
})
}
}
return result, nil
}
func (system *CryptoSystem) recordRevocation(rootId shared.CryptoSystemRootId, certificate *x509.Certificate) (*pkix.RevokedCertificate, error) {
databaseFile := system.Roots[rootId].DatabaseFile
_, err := os.Stat(databaseFile)
if err != nil {
return nil, fmt.Errorf("openssl certificate database file %s does not exist: %v", databaseFile, err)
}
inFile, err := os.Open(databaseFile)
if err != nil {
return nil, fmt.Errorf("could not open openssl certificate database file %s: %v", databaseFile, err)
}
defer func() { _ = inFile.Close() }()
outFile, err := ioutil.TempFile(path.Dir(databaseFile), "*.txt")
defer func() { _ = outFile.Close() }()
scanner := bufio.NewScanner(inFile)
writer := bufio.NewWriter(outFile)
found := false
revocationTime := time.Now()
for scanner.Scan() {
line := scanner.Text()
parts := strings.Split(line, "\t")
serialNumber, err := stringAsBigInt([]byte(parts[3]))
if err != nil {
return nil, fmt.Errorf("could not parse serial number %s as big int: %v", parts[3], err)
}
if serialNumber == certificate.SerialNumber {
line = strings.Join(
[]string{"R", parts[1], strconv.FormatInt(revocationTime.Unix(), 10) + "Z", parts[3], parts[4]},
"\t",
)
found = true
}
if _, err = writer.WriteString(fmt.Sprintf("%s\n", line)); err != nil {
return nil, fmt.Errorf("could not write '%s' to %s: %v", line, outFile.Name(), err)
}
}
if err = outFile.Close(); err != nil {
return nil, fmt.Errorf("could not close temporary file %s: %v", outFile.Name(), err)
}
if err = inFile.Close(); err != nil {
return nil, fmt.Errorf("could not close %s: %v", databaseFile, err)
}
if err = os.Rename(databaseFile, fmt.Sprintf("%s.old", databaseFile)); err != nil {
return nil, fmt.Errorf("could not rename %s to %s.old: %v", databaseFile, databaseFile, err)
}
if err = os.Rename(outFile.Name(), databaseFile); err != nil {
return nil, fmt.Errorf("could not rename temporary file %s to %s: %v", outFile.Name(), databaseFile, err)
}
if !found {
log.Warnf("entry not found in database")
}
return &pkix.RevokedCertificate{
SerialNumber: certificate.SerialNumber,
RevocationTime: revocationTime,
}, nil
}

View file

@ -43,7 +43,7 @@ func (p *PortHandler) MainLoop() {
return return
} }
log.Debugf("received command %v", command) log.Debugf("received command %v", command)
response, err := p.processor.Process(*command) response, err := p.processor.Process(command)
if err != nil { if err != nil {
log.Errorf("error processing command: %v", err) log.Errorf("error processing command: %v", err)
close(p.commandChan) close(p.commandChan)
@ -207,6 +207,6 @@ func NewSignerProcess(port io.ReadWriteCloser) *PortHandler {
port: port, port: port,
errors: errorChan, errors: errorChan,
commandChan: make(chan *datastructures.SignerRequest, 1), commandChan: make(chan *datastructures.SignerRequest, 1),
processor: &CommandProcessor{}, processor: NewCommandProcessor(),
} }
} }

131
signer/protocol_elements.go Normal file
View file

@ -0,0 +1,131 @@
package signer
import (
"crypto/x509"
"git.cacert.org/cacert-gosigner/shared"
)
const (
CsX509 shared.CryptoSystemId = 1
CsOpenPGP shared.CryptoSystemId = 2
)
const (
X509RootDefault shared.CryptoSystemRootId = 0
X509RootClass3 shared.CryptoSystemRootId = 1
X509RootClass3s shared.CryptoSystemRootId = 2
X509Root3 shared.CryptoSystemRootId = 3
X509Root4 shared.CryptoSystemRootId = 4
X509Root5 shared.CryptoSystemRootId = 5
)
const (
X509ProfileClient shared.CertificateProfileId = 0
X509ProfileClientOrg shared.CertificateProfileId = 1
X509ProfileClientCodesign shared.CertificateProfileId = 2
X509ProfileClientMachine shared.CertificateProfileId = 3
X509ProfileClientAds shared.CertificateProfileId = 4
X509ProfileServer shared.CertificateProfileId = 5
X509ProfileServerOrg shared.CertificateProfileId = 6
X509ProfileServerJabber shared.CertificateProfileId = 7
X509ProfileOCSP shared.CertificateProfileId = 8
X509ProfileTimestamp shared.CertificateProfileId = 9
X509ProfileProxy shared.CertificateProfileId = 10
X509ProfileSubCA shared.CertificateProfileId = 11
)
const (
X509MDDefault shared.MessageDigestAlgorithmId = 0
X509MDMd5 shared.MessageDigestAlgorithmId = 1
X509MDSha1 shared.MessageDigestAlgorithmId = 2
X509MDRipeMD160 shared.MessageDigestAlgorithmId = 3
X509MDSha256 shared.MessageDigestAlgorithmId = 8
X509MDSha384 shared.MessageDigestAlgorithmId = 9
X509MDSha512 shared.MessageDigestAlgorithmId = 10
)
const (
OpenPGPRoot0 shared.CryptoSystemRootId = 0
)
const (
OpenPGPDefaultProfile shared.CertificateProfileId = 0
)
const (
OpenPGPDefaultMD shared.MessageDigestAlgorithmId = 0
)
func NewCommandProcessor() *CommandProcessor {
cryptoSystems := make(map[shared.CryptoSystemId]*CryptoSystem)
cryptoSystems[CsX509] = &CryptoSystem{
Name: "X.509",
Roots: map[shared.CryptoSystemRootId]*RootCredentials{
X509RootDefault: {
Name: "openssl",
PrivateKeyFile: "/srv/ca/CA/private/ca.key.pem",
CertificateFile: "/srv/ca/CA/ca.crt.pem",
DatabaseFile: "/srv/ca/CA/index.txt",
CRLNumber: "/srv/ca/CA/crlnumber",
},
X509RootClass3: {
Name: "class3",
PrivateKeyFile: "/srv/ca/class3/private/ca.key.pem",
CertificateFile: "/srv/ca/class3/ca.crt.pem",
DatabaseFile: "/srv/ca/class3/index.txt",
CRLNumber: "/srv/ca/class3/crlnumber",
},
X509RootClass3s: {Name: "class3s"},
X509Root3: {Name: "root3"},
X509Root4: {Name: "root4"},
X509Root5: {Name: "root5"},
},
Profiles: map[shared.CertificateProfileId]string{
X509ProfileClient: "client",
X509ProfileClientOrg: "client-org",
X509ProfileClientCodesign: "client-codesign",
X509ProfileClientMachine: "client-machine",
X509ProfileClientAds: "client-ads",
X509ProfileServer: "server",
X509ProfileServerOrg: "server-org",
X509ProfileServerJabber: "server-jabber",
X509ProfileOCSP: "ocsp",
X509ProfileTimestamp: "timestamp",
X509ProfileProxy: "proxy",
X509ProfileSubCA: "subca",
},
// constants for openssl invocations. Should be replaced with
// something more useful
DigestAlgorithms: map[shared.MessageDigestAlgorithmId]x509.SignatureAlgorithm{
X509MDDefault: x509.SHA256WithRSA,
X509MDMd5: x509.MD5WithRSA,
X509MDSha1: x509.SHA1WithRSA,
X509MDRipeMD160: x509.UnknownSignatureAlgorithm,
X509MDSha256: x509.SHA256WithRSA,
X509MDSha384: x509.SHA384WithRSA,
X509MDSha512: x509.SHA512WithRSA,
},
}
cryptoSystems[CsOpenPGP] = &CryptoSystem{
Name: "OpenPGP",
Roots: map[shared.CryptoSystemRootId]*RootCredentials{
OpenPGPRoot0: {
Name: "OpenPGP Root",
PrivateKeyFile: "secring0.gpg",
PublicKeyFile: "pubring0.gpg",
},
},
Profiles: map[shared.CertificateProfileId]string{
OpenPGPDefaultProfile: "default",
},
// constants for gnupg cert-digest-algo parameter. Should be replaced with
// something more useful
DigestAlgorithms: map[shared.MessageDigestAlgorithmId]x509.SignatureAlgorithm{
OpenPGPDefaultMD: x509.SHA256WithRSA,
},
}
return &CommandProcessor{CryptoSystems: cryptoSystems, Settings: NewCommandProcessorSettings()}
}