diff --git a/ca/ca.go b/ca/ca.go index 2d5d1190829..4308d2dd6d2 100644 --- a/ca/ca.go +++ b/ca/ca.go @@ -669,6 +669,7 @@ func (ca *CertificateAuthorityImpl) issuePrecertificateInner(ctx context.Context } if err := csrlib.VerifyCSR( + ctx, csr, ca.maxNames, &ca.keyPolicy, diff --git a/cmd/boulder-ca/main.go b/cmd/boulder-ca/main.go index 65614039802..02813e79150 100644 --- a/cmd/boulder-ca/main.go +++ b/cmd/boulder-ca/main.go @@ -15,7 +15,7 @@ import ( pkcs11key "github.com/letsencrypt/pkcs11key/v4" "github.com/letsencrypt/boulder/ca" - "github.com/letsencrypt/boulder/ca/config" + ca_config "github.com/letsencrypt/boulder/ca/config" caPB "github.com/letsencrypt/boulder/ca/proto" "github.com/letsencrypt/boulder/cmd" "github.com/letsencrypt/boulder/core" @@ -155,9 +155,6 @@ func main() { issuers, err := loadIssuers(c) cmd.FailOnError(err, "Couldn't load issuers") - kp, err := goodkey.NewKeyPolicy(c.CA.WeakKeyFile, c.CA.BlockedKeyFile) - cmd.FailOnError(err, "Unable to create key policy") - tlsConfig, err := c.CA.TLS.Load() cmd.FailOnError(err, "TLS config") @@ -168,6 +165,13 @@ func main() { cmd.FailOnError(err, "Failed to load credentials and create gRPC connection to SA") sa := bgrpc.NewStorageAuthorityClient(sapb.NewStorageAuthorityClient(conn)) + var blockedKeyFunc goodkey.BlockedKeyCheckFunc + if features.Enabled(features.BlockedKeyTable) { + blockedKeyFunc = sa.KeyBlocked + } + kp, err := goodkey.NewKeyPolicy(c.CA.WeakKeyFile, c.CA.BlockedKeyFile, blockedKeyFunc) + cmd.FailOnError(err, "Unable to create key policy") + var orphanQueue *goque.Queue if c.CA.OrphanQueueDir != "" { orphanQueue, err = goque.OpenQueue(c.CA.OrphanQueueDir) diff --git a/cmd/boulder-ra/main.go b/cmd/boulder-ra/main.go index d7023737614..61cbabbaf3e 100644 --- a/cmd/boulder-ra/main.go +++ b/cmd/boulder-ra/main.go @@ -204,7 +204,11 @@ func main() { pendingAuthorizationLifetime = time.Duration(c.RA.PendingAuthorizationLifetimeDays) * 24 * time.Hour } - kp, err := goodkey.NewKeyPolicy(c.RA.WeakKeyFile, c.RA.BlockedKeyFile) + var blockedKeyFunc goodkey.BlockedKeyCheckFunc + if features.Enabled(features.BlockedKeyTable) { + blockedKeyFunc = sac.KeyBlocked + } + kp, err := goodkey.NewKeyPolicy(c.RA.WeakKeyFile, c.RA.BlockedKeyFile, blockedKeyFunc) cmd.FailOnError(err, "Unable to create key policy") if c.RA.MaxNames == 0 { diff --git a/cmd/boulder-wfe/main.go b/cmd/boulder-wfe/main.go index 377a5692562..b6fcff28a26 100644 --- a/cmd/boulder-wfe/main.go +++ b/cmd/boulder-wfe/main.go @@ -123,10 +123,14 @@ func main() { clk := cmd.Clock() + rac, sac, rns, npm := setupWFE(c, logger, stats, clk) + var blockedKeyFunc goodkey.BlockedKeyCheckFunc + if features.Enabled(features.BlockedKeyTable) { + blockedKeyFunc = sac.KeyBlocked + } // don't load any weak keys, but do load blocked keys - kp, err := goodkey.NewKeyPolicy("", c.WFE.BlockedKeyFile) + kp, err := goodkey.NewKeyPolicy("", c.WFE.BlockedKeyFile, blockedKeyFunc) cmd.FailOnError(err, "Unable to create key policy") - rac, sac, rns, npm := setupWFE(c, logger, stats, clk) wfe, err := wfe.NewWebFrontEndImpl(stats, clk, kp, rns, npm, logger) cmd.FailOnError(err, "Unable to create WFE") wfe.RA = rac diff --git a/cmd/boulder-wfe2/main.go b/cmd/boulder-wfe2/main.go index 1a68c903290..d1f76afbbf4 100644 --- a/cmd/boulder-wfe2/main.go +++ b/cmd/boulder-wfe2/main.go @@ -298,10 +298,14 @@ func main() { clk := cmd.Clock() + rac, sac, rns, npm := setupWFE(c, logger, stats, clk) + var blockedKeyFunc goodkey.BlockedKeyCheckFunc + if features.Enabled(features.BlockedKeyTable) { + blockedKeyFunc = sac.KeyBlocked + } // don't load any weak keys, but do load blocked keys - kp, err := goodkey.NewKeyPolicy("", c.WFE.BlockedKeyFile) + kp, err := goodkey.NewKeyPolicy("", c.WFE.BlockedKeyFile, blockedKeyFunc) cmd.FailOnError(err, "Unable to create key policy") - rac, sac, rns, npm := setupWFE(c, logger, stats, clk) if c.WFE.StaleTimeout.Duration == 0 { c.WFE.StaleTimeout.Duration = time.Minute * 10 diff --git a/core/interfaces.go b/core/interfaces.go index a835e9efa64..448f04b8b06 100644 --- a/core/interfaces.go +++ b/core/interfaces.go @@ -136,6 +136,7 @@ type StorageGetter interface { CountInvalidAuthorizations2(ctx context.Context, req *sapb.CountInvalidAuthorizationsRequest) (*sapb.Count, error) GetValidAuthorizations2(ctx context.Context, req *sapb.GetValidAuthorizationsRequest) (*sapb.Authorizations, error) SerialExists(ctx context.Context, req *sapb.Serial) (*sapb.Exists, error) + KeyBlocked(ctx context.Context, req *sapb.KeyBlockedRequest) (*sapb.Exists, error) } // StorageAdder are the Boulder SA's write/update methods @@ -155,6 +156,7 @@ type StorageAdder interface { NewAuthorizations2(ctx context.Context, req *sapb.AddPendingAuthorizationsRequest) (*sapb.Authorization2IDs, error) FinalizeAuthorization2(ctx context.Context, req *sapb.FinalizeAuthorizationRequest) error DeactivateAuthorization2(ctx context.Context, req *sapb.AuthorizationID2) (*corepb.Empty, error) + AddBlockedKey(ctx context.Context, req *sapb.AddBlockedKeyRequest) (*corepb.Empty, error) } // StorageAuthority interface represents a simple key/value diff --git a/csr/csr.go b/csr/csr.go index ce61a0b80e5..7e075ec7deb 100644 --- a/csr/csr.go +++ b/csr/csr.go @@ -1,6 +1,7 @@ package csr import ( + "context" "crypto" "crypto/x509" "strings" @@ -44,13 +45,13 @@ var ( // VerifyCSR checks the validity of a x509.CertificateRequest. Before doing checks it normalizes // the CSR which lowers the case of DNS names and subject CN, and if forceCNFromSAN is true it // will hoist a DNS name into the CN if it is empty. -func VerifyCSR(csr *x509.CertificateRequest, maxNames int, keyPolicy *goodkey.KeyPolicy, pa core.PolicyAuthority, forceCNFromSAN bool, regID int64) error { +func VerifyCSR(ctx context.Context, csr *x509.CertificateRequest, maxNames int, keyPolicy *goodkey.KeyPolicy, pa core.PolicyAuthority, forceCNFromSAN bool, regID int64) error { normalizeCSR(csr, forceCNFromSAN) key, ok := csr.PublicKey.(crypto.PublicKey) if !ok { return invalidPubKey } - if err := keyPolicy.GoodKey(key); err != nil { + if err := keyPolicy.GoodKey(ctx, key); err != nil { return berrors.BadPublicKeyError("invalid public key in CSR: %s", err) } if !goodSignatureAlgorithms[csr.SignatureAlgorithm] { diff --git a/csr/csr_test.go b/csr/csr_test.go index bc9d3deee6a..d356407ef76 100644 --- a/csr/csr_test.go +++ b/csr/csr_test.go @@ -1,6 +1,7 @@ package csr import ( + "context" "crypto/rand" "crypto/rsa" "crypto/x509" @@ -170,7 +171,7 @@ func TestVerifyCSR(t *testing.T) { } for _, c := range cases { - err := VerifyCSR(c.csr, c.maxNames, c.keyPolicy, c.pa, true, c.regID) + err := VerifyCSR(context.Background(), c.csr, c.maxNames, c.keyPolicy, c.pa, true, c.regID) test.AssertDeepEquals(t, c.expectedError, err) } } diff --git a/features/featureflag_string.go b/features/featureflag_string.go index 8b8dae9eb8e..6f7aa42e08f 100644 --- a/features/featureflag_string.go +++ b/features/featureflag_string.go @@ -26,11 +26,12 @@ func _() { _ = x[StripDefaultSchemePort-15] _ = x[StoreIssuerInfo-16] _ = x[StoreKeyHashes-17] + _ = x[BlockedKeyTable-18] } -const _FeatureFlag_name = "unusedWriteIssuedNamesPrecertHeadNonceStatusOKRemoveWFE2AccountIDCheckRenewalFirstParallelCheckFailedValidationDeleteUnusedChallengesCAAValidationMethodsCAAAccountURIEnforceMultiVAMultiVAFullResultsMandatoryPOSTAsGETAllowV1RegistrationV1DisableNewValidationsPrecertificateRevocationStripDefaultSchemePortStoreIssuerInfoStoreKeyHashes" +const _FeatureFlag_name = "unusedWriteIssuedNamesPrecertHeadNonceStatusOKRemoveWFE2AccountIDCheckRenewalFirstParallelCheckFailedValidationDeleteUnusedChallengesCAAValidationMethodsCAAAccountURIEnforceMultiVAMultiVAFullResultsMandatoryPOSTAsGETAllowV1RegistrationV1DisableNewValidationsPrecertificateRevocationStripDefaultSchemePortStoreIssuerInfoStoreKeyHashesBlockedKeyTable" -var _FeatureFlag_index = [...]uint16{0, 6, 29, 46, 65, 82, 111, 133, 153, 166, 180, 198, 216, 235, 258, 282, 304, 319, 333} +var _FeatureFlag_index = [...]uint16{0, 6, 29, 46, 65, 82, 111, 133, 153, 166, 180, 198, 216, 235, 258, 282, 304, 319, 333, 348} func (i FeatureFlag) String() string { if i < 0 || i >= FeatureFlag(len(_FeatureFlag_index)-1) { diff --git a/features/features.go b/features/features.go index d95139b6c55..0414ccee317 100644 --- a/features/features.go +++ b/features/features.go @@ -49,6 +49,9 @@ const ( StoreIssuerInfo // StoreKeyHashes enables storage of SPKI hashes associated with certificates. StoreKeyHashes + // BlockedKeyTable enables storage, and checking, of the blockedKeys table in addition + // to the blocked key list + BlockedKeyTable ) // List of features and their default value, protected by fMu @@ -71,6 +74,7 @@ var features = map[FeatureFlag]bool{ StoreIssuerInfo: false, WriteIssuedNamesPrecert: false, StoreKeyHashes: false, + BlockedKeyTable: false, } var fMu = new(sync.RWMutex) diff --git a/goodkey/blocked_test.go b/goodkey/blocked_test.go index e838e8bafbf..1bc7765ca23 100644 --- a/goodkey/blocked_test.go +++ b/goodkey/blocked_test.go @@ -1,6 +1,7 @@ package goodkey import ( + "context" "crypto" "io/ioutil" "os" @@ -85,7 +86,7 @@ func TestBlockedKeys(t *testing.T) { // All of the test keys should not be considered blocked for _, k := range blockedKeys { - err := testingPolicy.GoodKey(k) + err := testingPolicy.GoodKey(context.Background(), k) test.AssertNotError(t, err, "test key was blocked by key policy without block list") } @@ -95,7 +96,7 @@ func TestBlockedKeys(t *testing.T) { // Now all of the test keys should be considered blocked, and with the correct // type of error. for _, k := range blockedKeys { - err := testingPolicy.GoodKey(k) + err := testingPolicy.GoodKey(context.Background(), k) test.AssertError(t, err, "test key was not blocked by key policy with block list") test.Assert(t, berrors.Is(err, berrors.BadPublicKey), "err was not BadPublicKey error") } diff --git a/goodkey/good_key.go b/goodkey/good_key.go index 002f4999459..ccd1322f5cd 100644 --- a/goodkey/good_key.go +++ b/goodkey/good_key.go @@ -1,6 +1,7 @@ package goodkey import ( + "context" "crypto" "crypto/ecdsa" "crypto/elliptic" @@ -8,7 +9,10 @@ import ( "math/big" "sync" + "github.com/letsencrypt/boulder/core" berrors "github.com/letsencrypt/boulder/errors" + sapb "github.com/letsencrypt/boulder/sa/proto" + "github.com/titanous/rocacheck" ) @@ -34,6 +38,11 @@ var ( smallPrimes []*big.Int ) +// BlockedKeyCheckFunc is used to pass in the sa.BlockedKey method to KeyPolicy, +// rather than storing a full sa.SQLStorageAuthority. This makes testing +// significantly simpler. +type BlockedKeyCheckFunc func(context.Context, *sapb.KeyBlockedRequest) (*sapb.Exists, error) + // KeyPolicy determines which types of key may be used with various boulder // operations. type KeyPolicy struct { @@ -42,6 +51,7 @@ type KeyPolicy struct { AllowECDSANISTP384 bool // Whether ECDSA NISTP384 keys should be allowed. weakRSAList *WeakRSAKeys blockedList *blockedKeys + dbCheck BlockedKeyCheckFunc } // NewKeyPolicy returns a KeyPolicy that allows RSA, ECDSA256 and ECDSA384. @@ -51,11 +61,12 @@ type KeyPolicy struct { // containing Base64 encoded SHA256 hashes of pkix subject public keys that // should be blocked. If this argument is empty then no blocked key checking is // performed. -func NewKeyPolicy(weakKeyFile, blockedKeyFile string) (KeyPolicy, error) { +func NewKeyPolicy(weakKeyFile, blockedKeyFile string, bkc BlockedKeyCheckFunc) (KeyPolicy, error) { kp := KeyPolicy{ AllowRSA: true, AllowECDSANISTP256: true, AllowECDSANISTP384: true, + dbCheck: bkc, } if weakKeyFile != "" { keyList, err := LoadWeakRSASuffixes(weakKeyFile) @@ -79,7 +90,7 @@ func NewKeyPolicy(weakKeyFile, blockedKeyFile string) (KeyPolicy, error) { // strength and algorithm checking. GoodKey only supports pointers: *rsa.PublicKey // and *ecdsa.PublicKey. It will reject non-pointer types. // TODO: Support JSONWebKeys once go-jose migration is done. -func (policy *KeyPolicy) GoodKey(key crypto.PublicKey) error { +func (policy *KeyPolicy) GoodKey(ctx context.Context, key crypto.PublicKey) error { // If there is a blocked list configured then check if the public key is one // that has been administratively blocked. if policy.blockedList != nil { @@ -89,6 +100,19 @@ func (policy *KeyPolicy) GoodKey(key crypto.PublicKey) error { return berrors.BadPublicKeyError("public key is forbidden") } } + if policy.dbCheck != nil { + digest, err := core.KeyDigest(key) + if err != nil { + return err + } + exists, err := policy.dbCheck(ctx, &sapb.KeyBlockedRequest{KeyHash: digest[:]}) + if err != nil { + return err + } + if *exists.Exists { + return berrors.BadPublicKeyError("public key is forbidden") + } + } switch t := key.(type) { case *rsa.PublicKey: return policy.goodKeyRSA(t) diff --git a/goodkey/good_key_test.go b/goodkey/good_key_test.go index 005e11ab478..fc913c3c423 100644 --- a/goodkey/good_key_test.go +++ b/goodkey/good_key_test.go @@ -1,6 +1,7 @@ package goodkey import ( + "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -9,6 +10,8 @@ import ( "math/big" "testing" + berrors "github.com/letsencrypt/boulder/errors" + sapb "github.com/letsencrypt/boulder/sa/proto" "github.com/letsencrypt/boulder/test" ) @@ -20,13 +23,13 @@ var testingPolicy = &KeyPolicy{ func TestUnknownKeyType(t *testing.T) { notAKey := struct{}{} - err := testingPolicy.GoodKey(notAKey) + err := testingPolicy.GoodKey(context.Background(), notAKey) test.AssertError(t, err, "Should have rejected a key of unknown type") test.AssertEquals(t, err.Error(), "unknown key type struct {}") } func TestNilKey(t *testing.T) { - err := testingPolicy.GoodKey(nil) + err := testingPolicy.GoodKey(context.Background(), nil) test.AssertError(t, err, "Should have rejected a nil key") test.AssertEquals(t, err.Error(), "unknown key type ") } @@ -41,7 +44,7 @@ func TestSmallModulus(t *testing.T) { if !ok { t.Errorf("error parsing pubkey modulus") } - err := testingPolicy.GoodKey(&pubKey) + err := testingPolicy.GoodKey(context.Background(), &pubKey) test.AssertError(t, err, "Should have rejected too-short key") test.AssertEquals(t, err.Error(), "key too small: 2040") } @@ -56,7 +59,7 @@ func TestLargeModulus(t *testing.T) { if !ok { t.Errorf("error parsing pubkey modulus") } - err := testingPolicy.GoodKey(&pubKey) + err := testingPolicy.GoodKey(context.Background(), &pubKey) test.AssertError(t, err, "Should have rejected too-long key") test.AssertEquals(t, err.Error(), "key too large: 4097 > 4096") } @@ -67,7 +70,7 @@ func TestModulusModulo8(t *testing.T) { N: bigOne.Lsh(bigOne, 2048), E: 5, } - err := testingPolicy.GoodKey(&key) + err := testingPolicy.GoodKey(context.Background(), &key) test.AssertError(t, err, "Should have rejected modulus with length not divisible by 8") test.AssertEquals(t, err.Error(), "key length wasn't a multiple of 8: 2049") } @@ -80,7 +83,7 @@ func TestNonStandardExp(t *testing.T) { N: evenMod, E: (1 << 16), } - err := testingPolicy.GoodKey(&key) + err := testingPolicy.GoodKey(context.Background(), &key) test.AssertError(t, err, "Should have rejected non-standard exponent") test.AssertEquals(t, err.Error(), "key exponent must be 65537") } @@ -91,7 +94,7 @@ func TestEvenModulus(t *testing.T) { N: evenMod, E: (1 << 16) + 1, } - err := testingPolicy.GoodKey(&key) + err := testingPolicy.GoodKey(context.Background(), &key) test.AssertError(t, err, "Should have rejected even modulus") test.AssertEquals(t, err.Error(), "key divisible by small prime") } @@ -101,7 +104,7 @@ func TestModulusDivisibleBySmallPrime(t *testing.T) { N: mod2048, E: (1 << 16) + 1, } - err := testingPolicy.GoodKey(&key) + err := testingPolicy.GoodKey(context.Background(), &key) test.AssertError(t, err, "Should have rejected modulus divisible by 3") test.AssertEquals(t, err.Error(), "key divisible by small prime") } @@ -115,7 +118,7 @@ func TestROCA(t *testing.T) { N: n, E: 65537, } - err := testingPolicy.GoodKey(&key) + err := testingPolicy.GoodKey(context.Background(), &key) test.AssertError(t, err, "Should have rejected ROCA-weak key") test.AssertEquals(t, err.Error(), "key generated by vulnerable Infineon-based hardware") } @@ -123,14 +126,14 @@ func TestROCA(t *testing.T) { func TestGoodKey(t *testing.T) { private, err := rsa.GenerateKey(rand.Reader, 2048) test.AssertNotError(t, err, "Error generating key") - test.AssertNotError(t, testingPolicy.GoodKey(&private.PublicKey), "Should have accepted good key") + test.AssertNotError(t, testingPolicy.GoodKey(context.Background(), &private.PublicKey), "Should have accepted good key") } func TestECDSABadCurve(t *testing.T) { for _, curve := range invalidCurves { private, err := ecdsa.GenerateKey(curve, rand.Reader) test.AssertNotError(t, err, "Error generating key") - err = testingPolicy.GoodKey(&private.PublicKey) + err = testingPolicy.GoodKey(context.Background(), &private.PublicKey) test.AssertError(t, err, "Should have rejected key with unsupported curve") test.AssertEquals(t, err.Error(), fmt.Sprintf("ECDSA curve %s not allowed", curve.Params().Name)) } @@ -150,7 +153,7 @@ func TestECDSAGoodKey(t *testing.T) { for _, curve := range validCurves { private, err := ecdsa.GenerateKey(curve, rand.Reader) test.AssertNotError(t, err, "Error generating key") - test.AssertNotError(t, testingPolicy.GoodKey(&private.PublicKey), "Should have accepted good key") + test.AssertNotError(t, testingPolicy.GoodKey(context.Background(), &private.PublicKey), "Should have accepted good key") } } @@ -161,7 +164,7 @@ func TestECDSANotOnCurveX(t *testing.T) { test.AssertNotError(t, err, "Error generating key") private.X.Add(private.X, big.NewInt(1)) - err = testingPolicy.GoodKey(&private.PublicKey) + err = testingPolicy.GoodKey(context.Background(), &private.PublicKey) test.AssertError(t, err, "Should not have accepted key not on the curve") test.AssertEquals(t, err.Error(), "key point is not on the curve") } @@ -175,7 +178,7 @@ func TestECDSANotOnCurveY(t *testing.T) { // Change the public key so that it is no longer on the curve. private.Y.Add(private.Y, big.NewInt(1)) - err = testingPolicy.GoodKey(&private.PublicKey) + err = testingPolicy.GoodKey(context.Background(), &private.PublicKey) test.AssertError(t, err, "Should not have accepted key not on the curve") test.AssertEquals(t, err.Error(), "key point is not on the curve") } @@ -188,14 +191,14 @@ func TestECDSANegative(t *testing.T) { test.AssertNotError(t, err, "Error generating key") private.X.Neg(private.X) - err = testingPolicy.GoodKey(&private.PublicKey) + err = testingPolicy.GoodKey(context.Background(), &private.PublicKey) test.AssertError(t, err, "Should not have accepted key with negative X") test.AssertEquals(t, err.Error(), "key x, y must not be negative") // Check that negative Y is not accepted. private.X.Neg(private.X) private.Y.Neg(private.Y) - err = testingPolicy.GoodKey(&private.PublicKey) + err = testingPolicy.GoodKey(context.Background(), &private.PublicKey) test.AssertError(t, err, "Should not have accepted key with negative Y") test.AssertEquals(t, err.Error(), "key x, y must not be negative") } @@ -208,7 +211,7 @@ func TestECDSAXOutsideField(t *testing.T) { test.AssertNotError(t, err, "Error generating key") private.X.Mul(private.X, private.Curve.Params().P) - err = testingPolicy.GoodKey(&private.PublicKey) + err = testingPolicy.GoodKey(context.Background(), &private.PublicKey) test.AssertError(t, err, "Should not have accepted key with a X > p-1") test.AssertEquals(t, err.Error(), "key x, y must not exceed P-1") } @@ -221,7 +224,7 @@ func TestECDSAYOutsideField(t *testing.T) { test.AssertNotError(t, err, "Error generating key") private.X.Mul(private.Y, private.Curve.Params().P) - err = testingPolicy.GoodKey(&private.PublicKey) + err = testingPolicy.GoodKey(context.Background(), &private.PublicKey) test.AssertError(t, err, "Should not have accepted key with a Y > p-1") test.AssertEquals(t, err.Error(), "key x, y must not exceed P-1") } @@ -236,7 +239,7 @@ func TestECDSAIdentity(t *testing.T) { Y: big.NewInt(0), } - err := testingPolicy.GoodKey(&public) + err := testingPolicy.GoodKey(context.Background(), &public) test.AssertError(t, err, "Should not have accepted key with point at infinity") test.AssertEquals(t, err.Error(), "key x, y must not be the point at infinity") } @@ -245,5 +248,25 @@ func TestECDSAIdentity(t *testing.T) { func TestNonRefKey(t *testing.T) { private, err := rsa.GenerateKey(rand.Reader, 2048) test.AssertNotError(t, err, "Error generating key") - test.AssertError(t, testingPolicy.GoodKey(private.PublicKey), "Accepted non-reference key") + test.AssertError(t, testingPolicy.GoodKey(context.Background(), private.PublicKey), "Accepted non-reference key") +} + +func TestDBBlacklist(t *testing.T) { + exists := false + testCheck := func(context.Context, *sapb.KeyBlockedRequest) (*sapb.Exists, error) { + return &sapb.Exists{Exists: &exists}, nil + } + + policy, err := NewKeyPolicy("", "", testCheck) + test.AssertNotError(t, err, "NewKeyPolicy failed") + + k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + test.AssertNotError(t, err, "ecdsa.GenerateKey failed") + err = policy.GoodKey(context.Background(), k.Public()) + test.AssertNotError(t, err, "GoodKey failed with a non-blocked key") + exists = true + err = policy.GoodKey(context.Background(), k.Public()) + test.AssertError(t, err, "GoodKey didn't fail with a blocked key") + test.Assert(t, berrors.Is(err, berrors.BadPublicKey), "returned error is wrong type") + test.AssertEquals(t, err.Error(), "public key is forbidden") } diff --git a/grpc/sa-wrappers.go b/grpc/sa-wrappers.go index 15bc9b80632..5b0c582f8da 100644 --- a/grpc/sa-wrappers.go +++ b/grpc/sa-wrappers.go @@ -500,6 +500,16 @@ func (sas StorageAuthorityClientWrapper) SerialExists(ctx context.Context, req * return res, nil } +func (sac StorageAuthorityClientWrapper) AddBlockedKey(ctx context.Context, req *sapb.AddBlockedKeyRequest) (*corepb.Empty, error) { + // All return checking is done at the call site + return sac.inner.AddBlockedKey(ctx, req) +} + +func (sac StorageAuthorityClientWrapper) KeyBlocked(ctx context.Context, req *sapb.KeyBlockedRequest) (*sapb.Exists, error) { + // All return checking is done at the call site + return sac.inner.KeyBlocked(ctx, req) +} + // StorageAuthorityServerWrapper is the gRPC version of a core.ServerAuthority server type StorageAuthorityServerWrapper struct { // TODO(#3119): Don't use core.StorageAuthority @@ -916,3 +926,13 @@ func (sas StorageAuthorityServerWrapper) SerialExists(ctx context.Context, req * } return sas.inner.SerialExists(ctx, req) } + +func (sas StorageAuthorityServerWrapper) AddBlockedKey(ctx context.Context, req *sapb.AddBlockedKeyRequest) (*corepb.Empty, error) { + // All request checking is done in the method + return sas.inner.AddBlockedKey(ctx, req) +} + +func (sas StorageAuthorityServerWrapper) KeyBlocked(ctx context.Context, req *sapb.KeyBlockedRequest) (*sapb.Exists, error) { + // All request checking is done in the method + return sas.inner.KeyBlocked(ctx, req) +} diff --git a/mocks/mocks.go b/mocks/mocks.go index 15f12466d6b..beeefb1cb40 100644 --- a/mocks/mocks.go +++ b/mocks/mocks.go @@ -700,6 +700,17 @@ func (sa *StorageAuthority) RevokeCertificate(ctx context.Context, req *sapb.Rev return nil } +// AddBlockedKey is a mock +func (sa *StorageAuthority) AddBlockedKey(context.Context, *sapb.AddBlockedKeyRequest) (*corepb.Empty, error) { + return &corepb.Empty{}, nil +} + +// KeyBlocked is a mock +func (sa *StorageAuthority) KeyBlocked(ctx context.Context, req *sapb.KeyBlockedRequest) (*sapb.Exists, error) { + exists := false + return &sapb.Exists{Exists: &exists}, nil +} + // Publisher is a mock type Publisher struct { // empty diff --git a/ra/ra.go b/ra/ra.go index 914a4ee0a6f..c12f7b58391 100644 --- a/ra/ra.go +++ b/ra/ra.go @@ -320,7 +320,7 @@ func (ra *RegistrationAuthorityImpl) checkRegistrationLimits(ctx context.Context // NewRegistration constructs a new Registration from a request. func (ra *RegistrationAuthorityImpl) NewRegistration(ctx context.Context, init core.Registration) (core.Registration, error) { - if err := ra.keyPolicy.GoodKey(init.Key.Key); err != nil { + if err := ra.keyPolicy.GoodKey(ctx, init.Key.Key); err != nil { return core.Registration{}, berrors.MalformedError("invalid public key: %s", err.Error()) } if err := ra.checkRegistrationLimits(ctx, init.InitialIP); err != nil { @@ -985,7 +985,7 @@ func (ra *RegistrationAuthorityImpl) FinalizeOrder(ctx context.Context, req *rap return nil, err } - if err := csrlib.VerifyCSR(csrOb, ra.maxNames, &ra.keyPolicy, ra.PA, ra.forceCNFromSAN, *req.Order.RegistrationID); err != nil { + if err := csrlib.VerifyCSR(ctx, csrOb, ra.maxNames, &ra.keyPolicy, ra.PA, ra.forceCNFromSAN, *req.Order.RegistrationID); err != nil { // VerifyCSR returns berror instances that can be passed through as-is // without wrapping. return nil, err @@ -1076,7 +1076,7 @@ func (ra *RegistrationAuthorityImpl) FinalizeOrder(ctx context.Context, req *rap // NewCertificate requests the issuance of a certificate. func (ra *RegistrationAuthorityImpl) NewCertificate(ctx context.Context, req core.CertificateRequest, regID int64) (core.Certificate, error) { // Verify the CSR - if err := csrlib.VerifyCSR(req.CSR, ra.maxNames, &ra.keyPolicy, ra.PA, ra.forceCNFromSAN, regID); err != nil { + if err := csrlib.VerifyCSR(ctx, req.CSR, ra.maxNames, &ra.keyPolicy, ra.PA, ra.forceCNFromSAN, regID); err != nil { return core.Certificate{}, berrors.MalformedError(err.Error()) } // NewCertificate provides an order ID of 0, indicating this is a classic ACME @@ -1687,10 +1687,10 @@ func revokeEvent(state, serial, cn string, names []string, revocationCode revoca // revokeCertificate generates a revoked OCSP response for the given certificate, stores // the revocation information, and purges OCSP request URLs from Akamai. -func (ra *RegistrationAuthorityImpl) revokeCertificate(ctx context.Context, cert x509.Certificate, code revocation.Reason) error { +func (ra *RegistrationAuthorityImpl) revokeCertificate(ctx context.Context, cert x509.Certificate, code revocation.Reason, source string, comment string) error { status := string(core.OCSPStatusRevoked) reason := int32(code) - revokedAt := time.Now().UnixNano() + revokedAt := ra.clk.Now().UnixNano() ocspResponse, err := ra.CA.GenerateOCSP(ctx, &caPB.GenerateOCSPRequest{ CertDER: cert.Raw, Status: &status, @@ -1713,6 +1713,23 @@ func (ra *RegistrationAuthorityImpl) revokeCertificate(ctx context.Context, cert if err != nil { return err } + if features.Enabled(features.BlockedKeyTable) && reason == revocation.KeyCompromise { + digest, err := core.KeyDigest(cert.PublicKey) + if err != nil { + return err + } + req := &sapb.AddBlockedKeyRequest{ + KeyHash: digest[:], + Added: &revokedAt, + Source: &source, + } + if comment != "" { + req.Comment = &comment + } + if _, err = ra.SA.AddBlockedKey(ctx, req); err != nil { + return err + } + } purgeURLs, err := akamai.GeneratePurgeURLs(cert.Raw, ra.issuer) if err != nil { return err @@ -1728,7 +1745,7 @@ func (ra *RegistrationAuthorityImpl) revokeCertificate(ctx context.Context, cert // RevokeCertificateWithReg terminates trust in the certificate provided. func (ra *RegistrationAuthorityImpl) RevokeCertificateWithReg(ctx context.Context, cert x509.Certificate, revocationCode revocation.Reason, regID int64) error { serialString := core.SerialToString(cert.SerialNumber) - err := ra.revokeCertificate(ctx, cert, revocationCode) + err := ra.revokeCertificate(ctx, cert, revocationCode, "API", "") state := "Failure" defer func() { @@ -1758,7 +1775,9 @@ func (ra *RegistrationAuthorityImpl) RevokeCertificateWithReg(ctx context.Contex // called from the admin-revoker tool. func (ra *RegistrationAuthorityImpl) AdministrativelyRevokeCertificate(ctx context.Context, cert x509.Certificate, revocationCode revocation.Reason, user string) error { serialString := core.SerialToString(cert.SerialNumber) - err := ra.revokeCertificate(ctx, cert, revocationCode) + // TODO(#4774): allow setting the comment via the RPC, format should be: + // "revoked by %s: %s", user, comment + err := ra.revokeCertificate(ctx, cert, revocationCode, "admin-revoker", fmt.Sprintf("revoked by %s", user)) state := "Failure" defer func() { diff --git a/ra/ra_test.go b/ra/ra_test.go index 4351a50a75f..b20df1bad80 100644 --- a/ra/ra_test.go +++ b/ra/ra_test.go @@ -1,6 +1,7 @@ package ra import ( + "bytes" "context" "crypto/ecdsa" "crypto/elliptic" @@ -28,6 +29,7 @@ import ( ctx509 "github.com/google/certificate-transparency-go/x509" ctpkix "github.com/google/certificate-transparency-go/x509/pkix" "github.com/jmhodges/clock" + akamaipb "github.com/letsencrypt/boulder/akamai/proto" capb "github.com/letsencrypt/boulder/ca/proto" "github.com/letsencrypt/boulder/cmd" "github.com/letsencrypt/boulder/core" @@ -47,6 +49,7 @@ import ( pubpb "github.com/letsencrypt/boulder/publisher/proto" rapb "github.com/letsencrypt/boulder/ra/proto" "github.com/letsencrypt/boulder/ratelimit" + "github.com/letsencrypt/boulder/revocation" "github.com/letsencrypt/boulder/sa" sapb "github.com/letsencrypt/boulder/sa/proto" "github.com/letsencrypt/boulder/test" @@ -3809,3 +3812,74 @@ xGUhoOJp0T++nz6R3TX7Rwk7KmG6xX3vWr/MFu5A3c8fvkqj987Vti5BeBezCXfs rA== -----END CERTIFICATE----- `) + +type mockSABlockedKey struct { + mocks.StorageAuthority + + added *sapb.AddBlockedKeyRequest +} + +func (msabk *mockSABlockedKey) AddBlockedKey(_ context.Context, req *sapb.AddBlockedKeyRequest) (*corepb.Empty, error) { + msabk.added = req + return &corepb.Empty{}, nil +} + +type mockCAOCSP struct { + mocks.MockCA +} + +func (mcao *mockCAOCSP) GenerateOCSP(context.Context, *capb.GenerateOCSPRequest) (*capb.OCSPResponse, error) { + return &capb.OCSPResponse{Response: []byte{1, 2, 3}}, nil +} + +type mockPurger struct{} + +func (mp *mockPurger) Purge(context.Context, *akamaipb.PurgeRequest, ...grpc.CallOption) (*corepb.Empty, error) { + return &corepb.Empty{}, nil +} + +func TestRevocationAddBlockedKey(t *testing.T) { + _, _, ra, _, cleanUp := initAuthorities(t) + defer cleanUp() + + err := features.Set(map[string]bool{"BlockedKeyTable": true}) + test.AssertNotError(t, err, "features.Set failed") + defer features.Reset() + + mockSA := mockSABlockedKey{} + ra.SA = &mockSA + ra.CA = &mockCAOCSP{} + ra.purger = &mockPurger{} + + k, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + test.AssertNotError(t, err, "ecdsa.GenerateKey failed") + digest, err := core.KeyDigest(k.Public()) + test.AssertNotError(t, err, "core.KeyDigest failed") + + template := x509.Certificate{PublicKey: k, SerialNumber: big.NewInt(257)} + der, err := x509.CreateCertificate(rand.Reader, &template, &template, k.Public(), k) + test.AssertNotError(t, err, "x509.CreateCertificate failed") + cert, err := x509.ParseCertificate(der) + test.AssertNotError(t, err, "x509.ParseCertificate failed") + ra.issuer = cert + + err = ra.RevokeCertificateWithReg(context.Background(), *cert, revocation.Unspecified, 0) + test.AssertNotError(t, err, "RevokeCertificateWithReg failed") + test.Assert(t, mockSA.added == nil, "blocked key was added when reason was not keyCompromise") + + err = ra.RevokeCertificateWithReg(context.Background(), *cert, revocation.KeyCompromise, 0) + test.AssertNotError(t, err, "RevokeCertificateWithReg failed") + test.Assert(t, mockSA.added != nil, "blocked key was not added when reason was keyCompromise") + test.Assert(t, bytes.Equal(digest[:], mockSA.added.KeyHash), "key hash mismatch") + test.AssertEquals(t, *mockSA.added.Source, "API") + test.Assert(t, mockSA.added.Comment == nil, "Comment is not nil") + + mockSA.added = nil + err = ra.AdministrativelyRevokeCertificate(context.Background(), *cert, revocation.KeyCompromise, "root") + test.AssertNotError(t, err, "AdministrativelyRevokeCertificate failed") + test.Assert(t, mockSA.added != nil, "blocked key was not added when reason was keyCompromise") + test.Assert(t, bytes.Equal(digest[:], mockSA.added.KeyHash), "key hash mismatch") + test.AssertEquals(t, *mockSA.added.Source, "admin-revoker") + test.Assert(t, mockSA.added.Comment != nil, "Comment is nil") + test.AssertEquals(t, *mockSA.added.Comment, "revoked by root") +} diff --git a/sa/_db-next/migrations/20200414124347_AddBlockedKeysTable.sql b/sa/_db-next/migrations/20200414124347_AddBlockedKeysTable.sql new file mode 100644 index 00000000000..fd5d3c7fbbe --- /dev/null +++ b/sa/_db-next/migrations/20200414124347_AddBlockedKeysTable.sql @@ -0,0 +1,17 @@ + +-- +goose Up +-- SQL in section 'Up' is executed when this migration is applied + +CREATE TABLE `blockedKeys` ( + `id` bigint(20) NOT NULL AUTO_INCREMENT, + `keyHash` binary(32) NOT NULL UNIQUE, + `added` datetime NOT NULL, + `source` tinyint NOT NULL, + `comment` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; + +-- +goose Down +-- SQL section 'Down' is executed when this migration is rolled back + +DROP TABLE `blockedKeys`; diff --git a/sa/database.go b/sa/database.go index 0365102b80e..139f5840c86 100644 --- a/sa/database.go +++ b/sa/database.go @@ -140,4 +140,5 @@ func initTables(dbMap *gorp.DbMap) { dbMap.AddTableWithName(recordedSerialModel{}, "serials").SetKeys(true, "ID") dbMap.AddTableWithName(precertificateModel{}, "precertificates").SetKeys(true, "ID") dbMap.AddTableWithName(keyHashModel{}, "keyHashToSerial").SetKeys(true, "ID") + dbMap.AddTableWithName(blockedKeyModel{}, "blockedKeys").SetKeys(true, "ID") } diff --git a/sa/model.go b/sa/model.go index 22e9dbff031..a97e7ca0a03 100644 --- a/sa/model.go +++ b/sa/model.go @@ -617,3 +617,16 @@ type keyHashModel struct { CertNotAfter time.Time CertSerial string } + +var stringToSourceInt = map[string]int{ + "API": 1, + "admin-revoker": 2, +} + +type blockedKeyModel struct { + ID int64 + KeyHash []byte + Added time.Time + Source int + Comment *string +} diff --git a/sa/proto/sa.pb.go b/sa/proto/sa.pb.go index ade241e435b..d4f2181a13f 100644 --- a/sa/proto/sa.pb.go +++ b/sa/proto/sa.pb.go @@ -1804,6 +1804,108 @@ func (m *FinalizeAuthorizationRequest) GetValidationError() *proto1.ProblemDetai return nil } +type AddBlockedKeyRequest struct { + KeyHash []byte `protobuf:"bytes,1,opt,name=keyHash" json:"keyHash,omitempty"` + Added *int64 `protobuf:"varint,2,opt,name=added" json:"added,omitempty"` + Source *string `protobuf:"bytes,3,opt,name=source" json:"source,omitempty"` + Comment *string `protobuf:"bytes,4,opt,name=comment" json:"comment,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *AddBlockedKeyRequest) Reset() { *m = AddBlockedKeyRequest{} } +func (m *AddBlockedKeyRequest) String() string { return proto.CompactTextString(m) } +func (*AddBlockedKeyRequest) ProtoMessage() {} +func (*AddBlockedKeyRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_099fb35e782a48a6, []int{33} +} + +func (m *AddBlockedKeyRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_AddBlockedKeyRequest.Unmarshal(m, b) +} +func (m *AddBlockedKeyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_AddBlockedKeyRequest.Marshal(b, m, deterministic) +} +func (m *AddBlockedKeyRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_AddBlockedKeyRequest.Merge(m, src) +} +func (m *AddBlockedKeyRequest) XXX_Size() int { + return xxx_messageInfo_AddBlockedKeyRequest.Size(m) +} +func (m *AddBlockedKeyRequest) XXX_DiscardUnknown() { + xxx_messageInfo_AddBlockedKeyRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_AddBlockedKeyRequest proto.InternalMessageInfo + +func (m *AddBlockedKeyRequest) GetKeyHash() []byte { + if m != nil { + return m.KeyHash + } + return nil +} + +func (m *AddBlockedKeyRequest) GetAdded() int64 { + if m != nil && m.Added != nil { + return *m.Added + } + return 0 +} + +func (m *AddBlockedKeyRequest) GetSource() string { + if m != nil && m.Source != nil { + return *m.Source + } + return "" +} + +func (m *AddBlockedKeyRequest) GetComment() string { + if m != nil && m.Comment != nil { + return *m.Comment + } + return "" +} + +type KeyBlockedRequest struct { + KeyHash []byte `protobuf:"bytes,1,opt,name=keyHash" json:"keyHash,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *KeyBlockedRequest) Reset() { *m = KeyBlockedRequest{} } +func (m *KeyBlockedRequest) String() string { return proto.CompactTextString(m) } +func (*KeyBlockedRequest) ProtoMessage() {} +func (*KeyBlockedRequest) Descriptor() ([]byte, []int) { + return fileDescriptor_099fb35e782a48a6, []int{34} +} + +func (m *KeyBlockedRequest) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_KeyBlockedRequest.Unmarshal(m, b) +} +func (m *KeyBlockedRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_KeyBlockedRequest.Marshal(b, m, deterministic) +} +func (m *KeyBlockedRequest) XXX_Merge(src proto.Message) { + xxx_messageInfo_KeyBlockedRequest.Merge(m, src) +} +func (m *KeyBlockedRequest) XXX_Size() int { + return xxx_messageInfo_KeyBlockedRequest.Size(m) +} +func (m *KeyBlockedRequest) XXX_DiscardUnknown() { + xxx_messageInfo_KeyBlockedRequest.DiscardUnknown(m) +} + +var xxx_messageInfo_KeyBlockedRequest proto.InternalMessageInfo + +func (m *KeyBlockedRequest) GetKeyHash() []byte { + if m != nil { + return m.KeyHash + } + return nil +} + func init() { proto.RegisterType((*RegistrationID)(nil), "sa.RegistrationID") proto.RegisterType((*JSONWebKey)(nil), "sa.JSONWebKey") @@ -1841,121 +1943,129 @@ func init() { proto.RegisterType((*Authorization2IDs)(nil), "sa.Authorization2IDs") proto.RegisterType((*RevokeCertificateRequest)(nil), "sa.RevokeCertificateRequest") proto.RegisterType((*FinalizeAuthorizationRequest)(nil), "sa.FinalizeAuthorizationRequest") + proto.RegisterType((*AddBlockedKeyRequest)(nil), "sa.AddBlockedKeyRequest") + proto.RegisterType((*KeyBlockedRequest)(nil), "sa.KeyBlockedRequest") } func init() { proto.RegisterFile("sa/proto/sa.proto", fileDescriptor_099fb35e782a48a6) } var fileDescriptor_099fb35e782a48a6 = []byte{ - // 1739 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xdd, 0x6e, 0xdb, 0xc8, - 0x15, 0xd6, 0x8f, 0x65, 0x4b, 0xc7, 0x7f, 0xd2, 0xc4, 0x56, 0xb9, 0x8a, 0x9d, 0xc8, 0xb3, 0xd9, - 0xc0, 0x8b, 0x02, 0x5e, 0x97, 0x5d, 0xec, 0x16, 0x70, 0x9b, 0xc4, 0x8e, 0x1c, 0xc7, 0x69, 0x62, - 0x2b, 0x54, 0xe3, 0x16, 0x45, 0x6f, 0x18, 0x71, 0xa2, 0xb0, 0x91, 0x49, 0x65, 0x66, 0x64, 0x47, - 0xbe, 0x2e, 0xd0, 0x3e, 0x41, 0x51, 0xa0, 0x37, 0x7d, 0x8e, 0xbe, 0x44, 0x5f, 0xa9, 0x98, 0xc3, - 0x21, 0x45, 0x52, 0xa4, 0x5c, 0xa3, 0x45, 0xef, 0xe6, 0x9c, 0x39, 0x7f, 0x33, 0x73, 0x7e, 0x3e, - 0x12, 0x1a, 0xc2, 0xfe, 0x6e, 0xc4, 0x7d, 0xe9, 0x7f, 0x27, 0xec, 0x3d, 0x5c, 0x90, 0x92, 0xb0, - 0x5b, 0x9b, 0x7d, 0x9f, 0x33, 0xbd, 0xa1, 0x96, 0xc1, 0x16, 0x6d, 0xc3, 0x9a, 0xc5, 0x06, 0xae, - 0x90, 0xdc, 0x96, 0xae, 0xef, 0x9d, 0x76, 0xc8, 0x1a, 0x94, 0x5c, 0xc7, 0x28, 0xb6, 0x8b, 0xbb, - 0x65, 0xab, 0xe4, 0x3a, 0xf4, 0x01, 0xc0, 0xab, 0xde, 0xf9, 0xd9, 0x6f, 0xd9, 0xfb, 0x5f, 0xb3, - 0x09, 0xa9, 0x43, 0xf9, 0x8f, 0xd7, 0x9f, 0x70, 0x7b, 0xc5, 0x52, 0x4b, 0xba, 0x03, 0xeb, 0x87, - 0x63, 0xf9, 0xd1, 0xe7, 0xee, 0xcd, 0xac, 0x89, 0x1a, 0x9a, 0xf8, 0x67, 0x11, 0x1e, 0x9c, 0x30, - 0xd9, 0x65, 0x9e, 0xe3, 0x7a, 0x83, 0x84, 0xb4, 0xc5, 0x3e, 0x8f, 0x99, 0x90, 0xe4, 0x31, 0xac, - 0xf1, 0x44, 0x1c, 0x3a, 0x82, 0x14, 0x57, 0xc9, 0xb9, 0x0e, 0xf3, 0xa4, 0xfb, 0xc1, 0x65, 0xfc, - 0x37, 0x93, 0x11, 0x33, 0x4a, 0xe8, 0x26, 0xc5, 0x25, 0xbb, 0xb0, 0x3e, 0xe5, 0x5c, 0xd8, 0xc3, - 0x31, 0x33, 0xca, 0x28, 0x98, 0x66, 0x93, 0x07, 0x00, 0x57, 0xf6, 0xd0, 0x75, 0xde, 0x79, 0xd2, - 0x1d, 0x1a, 0x0b, 0xe8, 0x35, 0xc6, 0xa1, 0x02, 0xb6, 0x4f, 0x98, 0xbc, 0x50, 0x8c, 0x44, 0xe4, - 0xe2, 0xae, 0xa1, 0x1b, 0xb0, 0xe4, 0xf8, 0x97, 0xb6, 0xeb, 0x09, 0xa3, 0xd4, 0x2e, 0xef, 0xd6, - 0xac, 0x90, 0x54, 0x97, 0xea, 0xf9, 0xd7, 0x18, 0x60, 0xd9, 0x52, 0x4b, 0xfa, 0x8f, 0x22, 0xdc, - 0xcb, 0x70, 0x49, 0x7e, 0x01, 0x15, 0x0c, 0xcd, 0x28, 0xb6, 0xcb, 0xbb, 0xcb, 0x26, 0xdd, 0x13, - 0xf6, 0x5e, 0x86, 0xdc, 0xde, 0x1b, 0x7b, 0x74, 0x3c, 0x64, 0x97, 0xcc, 0x93, 0x56, 0xa0, 0xd0, - 0x3a, 0x07, 0x98, 0x32, 0x49, 0x13, 0x16, 0x03, 0xe7, 0xfa, 0x95, 0x34, 0x45, 0xbe, 0x85, 0x8a, - 0x3d, 0x96, 0x1f, 0x6f, 0xf0, 0x56, 0x97, 0xcd, 0x7b, 0x7b, 0x98, 0x2a, 0xc9, 0x17, 0x0b, 0x24, - 0xe8, 0xbf, 0x4a, 0xd0, 0x78, 0xce, 0xb8, 0xba, 0xca, 0xbe, 0x2d, 0x59, 0x4f, 0xda, 0x72, 0x2c, - 0x94, 0x61, 0xc1, 0xb8, 0x6b, 0x0f, 0x43, 0xc3, 0x01, 0x85, 0x7c, 0x94, 0xd0, 0xcf, 0xa0, 0x29, - 0xf5, 0x4e, 0x7e, 0x5f, 0x8c, 0x5e, 0xdb, 0x42, 0xbe, 0x1b, 0x39, 0xb6, 0x64, 0x8e, 0x7e, 0x82, - 0x34, 0x9b, 0xb4, 0x61, 0x99, 0xb3, 0x2b, 0xff, 0x13, 0x73, 0x3a, 0xb6, 0x64, 0x46, 0x05, 0xa5, - 0xe2, 0x2c, 0xf2, 0x08, 0x56, 0x35, 0x69, 0x31, 0x5b, 0xf8, 0x9e, 0xb1, 0x88, 0x32, 0x49, 0x26, - 0xf9, 0x1e, 0x36, 0x87, 0xb6, 0x90, 0xc7, 0x5f, 0x46, 0x6e, 0xf0, 0x34, 0x67, 0xf6, 0xa0, 0xc7, - 0x3c, 0x69, 0x2c, 0xa1, 0x74, 0xf6, 0x26, 0xa1, 0xb0, 0xa2, 0x02, 0xb2, 0x98, 0x18, 0xf9, 0x9e, - 0x60, 0x46, 0x15, 0x0b, 0x20, 0xc1, 0x23, 0x2d, 0xa8, 0x7a, 0xbe, 0x3c, 0xfc, 0x20, 0x19, 0x37, - 0x6a, 0x68, 0x2c, 0xa2, 0xc9, 0x16, 0xd4, 0x5c, 0x81, 0x66, 0x99, 0x63, 0x40, 0xbb, 0xb8, 0x5b, - 0xb5, 0xa6, 0x8c, 0x57, 0x0b, 0xd5, 0x52, 0xbd, 0x4c, 0xdb, 0xb0, 0xd8, 0x9b, 0xde, 0x56, 0xc6, - 0x2d, 0xd2, 0x03, 0xa8, 0x58, 0xb6, 0x37, 0x40, 0x57, 0xcc, 0xe6, 0x43, 0x97, 0x09, 0xa9, 0xb3, - 0x2d, 0xa2, 0x95, 0xf2, 0xd0, 0x96, 0x6a, 0xa7, 0x84, 0x3b, 0x9a, 0xa2, 0xdb, 0x50, 0x79, 0xee, - 0x8f, 0x3d, 0x49, 0x36, 0xa0, 0xd2, 0x57, 0x0b, 0xad, 0x19, 0x10, 0xf4, 0x77, 0xf0, 0x10, 0xb7, - 0x63, 0x6f, 0x2a, 0x8e, 0x26, 0x67, 0xf6, 0x25, 0x8b, 0x32, 0xfd, 0x21, 0x54, 0xb8, 0x72, 0x8f, - 0x8a, 0xcb, 0x66, 0x4d, 0x65, 0x1f, 0xc6, 0x63, 0x05, 0x7c, 0x65, 0xd9, 0x53, 0x0a, 0x3a, 0xc1, - 0x03, 0x82, 0xfe, 0xb9, 0x08, 0x2b, 0x68, 0x5a, 0x9b, 0x23, 0x4f, 0x61, 0xa5, 0x1f, 0xa3, 0x75, - 0x32, 0xdf, 0x57, 0xe6, 0xe2, 0x72, 0xf1, 0x2c, 0x4e, 0x28, 0xb4, 0x7e, 0x48, 0x24, 0x33, 0x81, - 0x05, 0xe5, 0x48, 0xdf, 0x15, 0xae, 0xa7, 0x67, 0x2c, 0xc5, 0xcf, 0xd8, 0x85, 0x6d, 0x74, 0x10, - 0x6f, 0x79, 0xe2, 0x68, 0x72, 0xda, 0x0d, 0x4f, 0xa8, 0x3a, 0xd7, 0x48, 0x77, 0xb7, 0x92, 0x3b, - 0x9a, 0x9e, 0xb8, 0x94, 0x7d, 0x62, 0xfa, 0x97, 0x22, 0xec, 0xa0, 0xc9, 0x53, 0xef, 0xea, 0xbf, - 0x6f, 0x11, 0x2d, 0xa8, 0x7e, 0xf4, 0x85, 0xc4, 0xd3, 0x04, 0x7d, 0x2d, 0xa2, 0xa7, 0xa1, 0x94, - 0x73, 0x42, 0xe9, 0x01, 0xc1, 0x48, 0xce, 0xb9, 0xc3, 0x78, 0xe4, 0x7a, 0x0b, 0x6a, 0x76, 0x1f, - 0x4f, 0x1f, 0x79, 0x9d, 0x32, 0x6e, 0x3f, 0xdf, 0x4b, 0xd8, 0x40, 0xa3, 0x2f, 0xde, 0x76, 0xce, - 0x7a, 0x4c, 0x46, 0x66, 0x9b, 0xb0, 0x78, 0xed, 0x7a, 0x8e, 0x7f, 0xad, 0x6d, 0x6a, 0x2a, 0xbf, - 0xc9, 0xd1, 0x7d, 0xd8, 0xd0, 0x46, 0x8e, 0xbf, 0xb8, 0x62, 0x6a, 0x29, 0xa6, 0x51, 0x4c, 0x6a, - 0x74, 0xa1, 0xdd, 0xe5, 0xec, 0xca, 0xf5, 0xc7, 0x22, 0x96, 0x94, 0x49, 0xed, 0xbc, 0x46, 0xb6, - 0x01, 0x15, 0xce, 0x06, 0xa7, 0x9d, 0xf0, 0xfd, 0x91, 0x50, 0x15, 0x16, 0xa8, 0x2b, 0x3d, 0x86, - 0x2b, 0xd4, 0xab, 0x5a, 0x9a, 0xa2, 0x12, 0xea, 0x87, 0x8e, 0x13, 0x94, 0x61, 0xe8, 0x23, 0xb2, - 0x55, 0x8c, 0xd9, 0x8a, 0xd5, 0x68, 0x29, 0xd1, 0xe9, 0x0c, 0x58, 0xea, 0x73, 0x86, 0x9d, 0x2c, - 0x68, 0xe8, 0x21, 0xa9, 0x76, 0x18, 0x16, 0xbc, 0xd0, 0x3d, 0x2e, 0x24, 0x55, 0x85, 0x6c, 0x1e, - 0x3a, 0x4e, 0xec, 0x94, 0xa1, 0xef, 0x3a, 0x94, 0x1d, 0xc6, 0xc3, 0x79, 0xeb, 0x30, 0x9e, 0x7d, - 0x32, 0x55, 0x03, 0xaa, 0x17, 0xa1, 0xcb, 0x15, 0x0b, 0xd7, 0x2a, 0x42, 0x57, 0x88, 0x71, 0xd4, - 0x52, 0x35, 0xa5, 0xb2, 0x0c, 0x57, 0xfc, 0xb4, 0xa3, 0xdb, 0x68, 0x44, 0xd3, 0x7d, 0x68, 0xa6, - 0x03, 0xd1, 0xdd, 0x4d, 0xdd, 0xb4, 0x3b, 0x08, 0x1b, 0x8e, 0xba, 0x69, 0xa4, 0x68, 0x17, 0x56, - 0x30, 0xe3, 0xe2, 0x25, 0x14, 0xc3, 0x0f, 0x64, 0x1f, 0xee, 0x8d, 0x05, 0xbb, 0x30, 0x93, 0x95, - 0x81, 0xd1, 0x57, 0xad, 0xac, 0x2d, 0xfa, 0x1a, 0x68, 0x38, 0x71, 0xd1, 0x72, 0x76, 0x4d, 0xa5, - 0xfd, 0x34, 0x61, 0xd1, 0xee, 0xf7, 0x65, 0x74, 0x31, 0x9a, 0xa2, 0x13, 0xf8, 0xc9, 0x09, 0x0b, - 0x8a, 0xe2, 0x85, 0xcf, 0x13, 0xfd, 0x6c, 0xaa, 0x52, 0x8c, 0xab, 0x64, 0xb7, 0xb1, 0xbc, 0x83, - 0x94, 0xf3, 0x0f, 0xf2, 0xb7, 0x22, 0x18, 0x27, 0x4c, 0xfe, 0xdf, 0x60, 0x83, 0x9a, 0xa6, 0x9c, - 0x7d, 0x1e, 0xbb, 0x5c, 0xc7, 0x72, 0x13, 0x64, 0x5a, 0xd5, 0x4a, 0xb3, 0xe9, 0x5f, 0x8b, 0xb0, - 0x96, 0xc2, 0x16, 0x3f, 0x0f, 0x67, 0x7f, 0xd0, 0x8e, 0xb7, 0x55, 0x2f, 0x98, 0x03, 0x2b, 0x50, - 0xf6, 0x7f, 0x0f, 0x2b, 0x5e, 0xc3, 0xc3, 0x43, 0xc7, 0xc9, 0x82, 0x8a, 0xd1, 0xcd, 0x7d, 0x9b, - 0x0c, 0x74, 0x9e, 0xb5, 0x47, 0x50, 0x4f, 0x81, 0x53, 0xbc, 0x36, 0xd7, 0x09, 0x9b, 0x8d, 0x5a, - 0x52, 0x3a, 0x23, 0x65, 0xce, 0xc0, 0xe0, 0x6f, 0xa0, 0x91, 0x90, 0x31, 0x53, 0xa6, 0xca, 0x81, - 0xa9, 0x1b, 0x30, 0x2c, 0x84, 0x1b, 0x19, 0xb5, 0x3c, 0x07, 0x1b, 0xf1, 0x00, 0xb0, 0xe8, 0xcc, - 0x0d, 0x28, 0x55, 0xd3, 0x0a, 0xfa, 0xe8, 0x07, 0xc6, 0xb5, 0xaa, 0x5d, 0x1e, 0x62, 0x90, 0x05, - 0xac, 0xf5, 0x88, 0xa6, 0x7f, 0x2a, 0xc1, 0xd6, 0x0b, 0xd7, 0xb3, 0x87, 0xee, 0x0d, 0xcb, 0x04, - 0xd9, 0x19, 0x25, 0xa3, 0x41, 0x59, 0x29, 0x01, 0xca, 0x62, 0x8d, 0xaa, 0x9c, 0x68, 0x54, 0x38, - 0x4d, 0xa4, 0x64, 0x97, 0xa3, 0x10, 0xa8, 0xd5, 0xac, 0x29, 0x83, 0x74, 0xa0, 0x81, 0x43, 0x50, - 0x3b, 0xed, 0xfb, 0xdc, 0x11, 0x46, 0x05, 0x1f, 0xa9, 0x19, 0x3c, 0xd2, 0x45, 0x6a, 0xdb, 0x9a, - 0x55, 0x20, 0x4f, 0x60, 0x7d, 0xca, 0x3c, 0xe6, 0xdc, 0xe7, 0x08, 0xe4, 0x96, 0xcd, 0x8d, 0xc0, - 0x46, 0x97, 0xfb, 0xef, 0x87, 0xec, 0xb2, 0xc3, 0xa4, 0xed, 0x0e, 0x85, 0x95, 0x16, 0x36, 0xff, - 0x4e, 0xa0, 0xde, 0x93, 0x3e, 0xb7, 0x07, 0xe1, 0x2d, 0xc8, 0x09, 0x39, 0x80, 0xf5, 0x13, 0x96, - 0x98, 0xfb, 0x84, 0xe0, 0xb0, 0x4b, 0x14, 0x5b, 0x8b, 0x04, 0x2e, 0xe2, 0x5c, 0x5a, 0x20, 0xbf, - 0x84, 0x8d, 0x94, 0xf2, 0xd1, 0x44, 0x7d, 0x0c, 0xad, 0x29, 0x0b, 0xd3, 0x8f, 0xa3, 0x1c, 0xed, - 0x9f, 0xc1, 0xda, 0x09, 0x8b, 0xc3, 0x2a, 0x02, 0x4a, 0x2f, 0x98, 0x31, 0xad, 0x46, 0xa0, 0x13, - 0xdb, 0xa6, 0x05, 0xf2, 0x3d, 0x34, 0xd4, 0xf7, 0x12, 0x67, 0xfd, 0xbb, 0x68, 0x1d, 0x60, 0x98, - 0xb3, 0x98, 0x3c, 0xae, 0xb8, 0x89, 0x20, 0x2b, 0x2d, 0x42, 0x0b, 0xa4, 0x07, 0x46, 0x1e, 0xfc, - 0x23, 0x5f, 0x47, 0xc8, 0x2c, 0x1f, 0x1c, 0xb6, 0xea, 0x69, 0xf8, 0x46, 0x0b, 0xe4, 0x25, 0x34, - 0xb3, 0xf1, 0x16, 0xd9, 0x89, 0xa4, 0xf3, 0xb0, 0x58, 0xab, 0x16, 0x89, 0xd0, 0x02, 0x79, 0x03, - 0xf7, 0x73, 0xa4, 0x11, 0x78, 0xde, 0xd5, 0x9c, 0x09, 0xcb, 0x31, 0xac, 0x44, 0x9a, 0xd1, 0x5e, - 0x02, 0x3c, 0x25, 0x75, 0x7e, 0x80, 0xd5, 0x04, 0x14, 0x22, 0x46, 0xb4, 0x9b, 0x42, 0x47, 0x49, - 0xbd, 0x1f, 0x61, 0x35, 0x01, 0x7c, 0x02, 0xbd, 0x2c, 0x2c, 0xd4, 0xc2, 0x97, 0x0a, 0x58, 0xb4, - 0x40, 0xce, 0xe1, 0xab, 0x5c, 0xfc, 0x43, 0x1e, 0x29, 0xd1, 0xdb, 0xe0, 0x51, 0xca, 0xe0, 0x33, - 0x4c, 0xab, 0x64, 0x1b, 0x23, 0x1b, 0x33, 0x7d, 0xfe, 0xb4, 0x63, 0xb6, 0xb2, 0x9a, 0x2a, 0x3e, - 0x28, 0x99, 0x19, 0x68, 0x26, 0xd9, 0x52, 0x26, 0xf2, 0x06, 0x5d, 0x8b, 0xcc, 0x0e, 0x12, 0x5a, - 0x20, 0xef, 0x70, 0x34, 0x66, 0xf5, 0x79, 0x93, 0x50, 0x6d, 0x6f, 0xce, 0x0f, 0x83, 0xbc, 0x00, - 0x9f, 0xe8, 0x3c, 0xc9, 0x1c, 0x20, 0x66, 0x66, 0xcd, 0x27, 0x1e, 0xeb, 0x0f, 0xb0, 0x35, 0x07, - 0x7b, 0x98, 0xe4, 0xb1, 0x0e, 0xed, 0x16, 0x74, 0x92, 0x73, 0xe8, 0xb7, 0x3a, 0xba, 0xcc, 0x8f, - 0x05, 0x93, 0x7c, 0x13, 0x45, 0x32, 0xef, 0x6b, 0x22, 0x19, 0xb0, 0x85, 0xf0, 0xe6, 0x22, 0xcb, - 0xdc, 0x4e, 0x3c, 0xd6, 0xbb, 0x84, 0x79, 0x00, 0xeb, 0x67, 0xec, 0x3a, 0xd5, 0x2c, 0x67, 0x5a, - 0x5b, 0x4e, 0xbb, 0xfb, 0x11, 0x48, 0xf0, 0xc9, 0x7e, 0xab, 0xfe, 0x72, 0xc0, 0x3b, 0xbe, 0x1c, - 0xc9, 0x09, 0x2d, 0x90, 0x53, 0x58, 0x4b, 0x42, 0x4f, 0xf2, 0x15, 0x46, 0x97, 0x85, 0x8b, 0x5b, - 0xad, 0xac, 0x2d, 0x3d, 0x07, 0x0b, 0xe4, 0x57, 0xd0, 0x50, 0x20, 0x22, 0xd9, 0x3f, 0xe7, 0x58, - 0x4b, 0x45, 0xb2, 0x0f, 0xb5, 0xe8, 0x23, 0x40, 0xd7, 0x47, 0xea, 0x9b, 0x20, 0xad, 0x71, 0x00, - 0xcd, 0x0e, 0xb3, 0xfb, 0xd2, 0xbd, 0x9a, 0x3d, 0xf8, 0x6c, 0xc6, 0xa5, 0x94, 0x1f, 0x43, 0xf5, - 0x8c, 0x5d, 0x63, 0x32, 0x11, 0xbd, 0x85, 0x44, 0x2b, 0x4e, 0x60, 0x58, 0xa4, 0xa7, 0x91, 0x6c, - 0x97, 0xfb, 0x7d, 0x26, 0x84, 0xeb, 0x0d, 0x32, 0x35, 0x42, 0xcb, 0x3f, 0x85, 0xd5, 0x50, 0x03, - 0x67, 0xe3, 0x6d, 0xc2, 0x21, 0x7a, 0xc8, 0x8f, 0x65, 0x2a, 0x5c, 0x0d, 0x51, 0x35, 0xc1, 0xce, - 0x1f, 0xff, 0x06, 0x48, 0x07, 0xfe, 0x04, 0xea, 0x69, 0x08, 0x4e, 0xee, 0xeb, 0xe4, 0xcc, 0x02, - 0xe6, 0x69, 0xfd, 0x67, 0xd0, 0x98, 0x01, 0x55, 0x41, 0xd3, 0xc9, 0xc3, 0x5a, 0xe9, 0x70, 0x2d, - 0x20, 0x67, 0xec, 0x3a, 0x5d, 0x20, 0x5f, 0xeb, 0xa7, 0x9d, 0x87, 0x36, 0x83, 0x89, 0x39, 0x03, - 0xfd, 0x30, 0x5f, 0x9b, 0x99, 0x68, 0xcb, 0x24, 0x6d, 0x6c, 0xf0, 0x73, 0x90, 0x58, 0x3a, 0xbc, - 0xa7, 0x60, 0x4c, 0xd3, 0xe7, 0x3f, 0xea, 0xcf, 0x29, 0x03, 0xbb, 0xb0, 0x12, 0xe4, 0xa7, 0x9e, - 0x0e, 0xf1, 0x91, 0x9f, 0x98, 0x01, 0x47, 0x4b, 0xbf, 0xaf, 0xe0, 0x9f, 0xdf, 0x7f, 0x07, 0x00, - 0x00, 0xff, 0xff, 0x71, 0xbc, 0xd9, 0x84, 0x28, 0x16, 0x00, 0x00, + // 1830 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xeb, 0x72, 0x1b, 0xb7, + 0x15, 0xe6, 0xc5, 0x94, 0xc9, 0xa3, 0x2b, 0x61, 0x99, 0xdd, 0xd0, 0xb2, 0x4d, 0x23, 0x8e, 0x47, + 0x99, 0x4e, 0x15, 0x67, 0x9b, 0x49, 0x32, 0xa3, 0xd6, 0x89, 0x14, 0xca, 0xb2, 0x62, 0x47, 0x66, + 0x96, 0xb5, 0xda, 0xe9, 0xf4, 0xcf, 0x86, 0x8b, 0xd0, 0x5b, 0x53, 0xbb, 0x0c, 0x00, 0x4a, 0xa6, + 0x7e, 0x77, 0xa6, 0x79, 0x82, 0x4e, 0x7f, 0xf6, 0x39, 0xfa, 0x12, 0x7d, 0xa5, 0x0e, 0x0e, 0xb0, + 0x57, 0xee, 0x52, 0xd5, 0xb4, 0xd3, 0x7f, 0x7b, 0x0e, 0xce, 0x0d, 0xc0, 0xb9, 0x7c, 0x58, 0x68, + 0x0b, 0xf7, 0x93, 0x29, 0x0f, 0x65, 0xf8, 0x89, 0x70, 0xf7, 0xf0, 0x83, 0xd4, 0x84, 0xdb, 0xbd, + 0x3b, 0x0a, 0x39, 0x33, 0x0b, 0xea, 0x53, 0x2f, 0xd1, 0x1e, 0x6c, 0x38, 0x6c, 0xec, 0x0b, 0xc9, + 0x5d, 0xe9, 0x87, 0xc1, 0x49, 0x9f, 0x6c, 0x40, 0xcd, 0xf7, 0xac, 0x6a, 0xaf, 0xba, 0x5b, 0x77, + 0x6a, 0xbe, 0x47, 0x1f, 0x00, 0x7c, 0x3b, 0x7c, 0x7d, 0xfa, 0x7b, 0xf6, 0xc3, 0x4b, 0x36, 0x27, + 0x5b, 0x50, 0xff, 0xf3, 0xe5, 0x3b, 0x5c, 0x5e, 0x73, 0xd4, 0x27, 0x7d, 0x04, 0x9b, 0x07, 0x33, + 0xf9, 0x36, 0xe4, 0xfe, 0xd5, 0xa2, 0x89, 0x16, 0x9a, 0xf8, 0x67, 0x15, 0x1e, 0x1c, 0x33, 0x39, + 0x60, 0x81, 0xe7, 0x07, 0xe3, 0x8c, 0xb4, 0xc3, 0x7e, 0x9a, 0x31, 0x21, 0xc9, 0x13, 0xd8, 0xe0, + 0x99, 0x38, 0x4c, 0x04, 0x39, 0xae, 0x92, 0xf3, 0x3d, 0x16, 0x48, 0xff, 0x47, 0x9f, 0xf1, 0xdf, + 0xcd, 0xa7, 0xcc, 0xaa, 0xa1, 0x9b, 0x1c, 0x97, 0xec, 0xc2, 0x66, 0xc2, 0x39, 0x73, 0x27, 0x33, + 0x66, 0xd5, 0x51, 0x30, 0xcf, 0x26, 0x0f, 0x00, 0x2e, 0xdc, 0x89, 0xef, 0xbd, 0x09, 0xa4, 0x3f, + 0xb1, 0x6e, 0xa1, 0xd7, 0x14, 0x87, 0x0a, 0xb8, 0x7f, 0xcc, 0xe4, 0x99, 0x62, 0x64, 0x22, 0x17, + 0x37, 0x0d, 0xdd, 0x82, 0xdb, 0x5e, 0x78, 0xee, 0xfa, 0x81, 0xb0, 0x6a, 0xbd, 0xfa, 0x6e, 0xcb, + 0x89, 0x48, 0x75, 0xa8, 0x41, 0x78, 0x89, 0x01, 0xd6, 0x1d, 0xf5, 0x49, 0xff, 0x51, 0x85, 0x3b, + 0x05, 0x2e, 0xc9, 0x97, 0xd0, 0xc0, 0xd0, 0xac, 0x6a, 0xaf, 0xbe, 0xbb, 0x6a, 0xd3, 0x3d, 0xe1, + 0xee, 0x15, 0xc8, 0xed, 0x7d, 0xe7, 0x4e, 0x8f, 0x26, 0xec, 0x9c, 0x05, 0xd2, 0xd1, 0x0a, 0xdd, + 0xd7, 0x00, 0x09, 0x93, 0x74, 0x60, 0x45, 0x3b, 0x37, 0xb7, 0x64, 0x28, 0xf2, 0x31, 0x34, 0xdc, + 0x99, 0x7c, 0x7b, 0x85, 0xa7, 0xba, 0x6a, 0xdf, 0xd9, 0xc3, 0x54, 0xc9, 0xde, 0x98, 0x96, 0xa0, + 0xff, 0xaa, 0x41, 0xfb, 0x1b, 0xc6, 0xd5, 0x51, 0x8e, 0x5c, 0xc9, 0x86, 0xd2, 0x95, 0x33, 0xa1, + 0x0c, 0x0b, 0xc6, 0x7d, 0x77, 0x12, 0x19, 0xd6, 0x14, 0xf2, 0x51, 0xc2, 0x5c, 0x83, 0xa1, 0xd4, + 0x3d, 0x85, 0x23, 0x31, 0x7d, 0xe5, 0x0a, 0xf9, 0x66, 0xea, 0xb9, 0x92, 0x79, 0xe6, 0x0a, 0xf2, + 0x6c, 0xd2, 0x83, 0x55, 0xce, 0x2e, 0xc2, 0x77, 0xcc, 0xeb, 0xbb, 0x92, 0x59, 0x0d, 0x94, 0x4a, + 0xb3, 0xc8, 0x63, 0x58, 0x37, 0xa4, 0xc3, 0x5c, 0x11, 0x06, 0xd6, 0x0a, 0xca, 0x64, 0x99, 0xe4, + 0x33, 0xb8, 0x3b, 0x71, 0x85, 0x3c, 0x7a, 0x3f, 0xf5, 0xf5, 0xd5, 0x9c, 0xba, 0xe3, 0x21, 0x0b, + 0xa4, 0x75, 0x1b, 0xa5, 0x8b, 0x17, 0x09, 0x85, 0x35, 0x15, 0x90, 0xc3, 0xc4, 0x34, 0x0c, 0x04, + 0xb3, 0x9a, 0x58, 0x00, 0x19, 0x1e, 0xe9, 0x42, 0x33, 0x08, 0xe5, 0xc1, 0x8f, 0x92, 0x71, 0xab, + 0x85, 0xc6, 0x62, 0x9a, 0xec, 0x40, 0xcb, 0x17, 0x68, 0x96, 0x79, 0x16, 0xf4, 0xaa, 0xbb, 0x4d, + 0x27, 0x61, 0x7c, 0x7b, 0xab, 0x59, 0xdb, 0xaa, 0xd3, 0x1e, 0xac, 0x0c, 0x93, 0xd3, 0x2a, 0x38, + 0x45, 0xba, 0x0f, 0x0d, 0xc7, 0x0d, 0xc6, 0xe8, 0x8a, 0xb9, 0x7c, 0xe2, 0x33, 0x21, 0x4d, 0xb6, + 0xc5, 0xb4, 0x52, 0x9e, 0xb8, 0x52, 0xad, 0xd4, 0x70, 0xc5, 0x50, 0xf4, 0x3e, 0x34, 0xbe, 0x09, + 0x67, 0x81, 0x24, 0xdb, 0xd0, 0x18, 0xa9, 0x0f, 0xa3, 0xa9, 0x09, 0xfa, 0x07, 0x78, 0x88, 0xcb, + 0xa9, 0x3b, 0x15, 0x87, 0xf3, 0x53, 0xf7, 0x9c, 0xc5, 0x99, 0xfe, 0x10, 0x1a, 0x5c, 0xb9, 0x47, + 0xc5, 0x55, 0xbb, 0xa5, 0xb2, 0x0f, 0xe3, 0x71, 0x34, 0x5f, 0x59, 0x0e, 0x94, 0x82, 0x49, 0x70, + 0x4d, 0xd0, 0xbf, 0x56, 0x61, 0x0d, 0x4d, 0x1b, 0x73, 0xe4, 0x2b, 0x58, 0x1b, 0xa5, 0x68, 0x93, + 0xcc, 0xf7, 0x94, 0xb9, 0xb4, 0x5c, 0x3a, 0x8b, 0x33, 0x0a, 0xdd, 0xcf, 0x33, 0xc9, 0x4c, 0xe0, + 0x96, 0x72, 0x64, 0xce, 0x0a, 0xbf, 0x93, 0x3d, 0xd6, 0xd2, 0x7b, 0x1c, 0xc0, 0x7d, 0x74, 0x90, + 0x6e, 0x79, 0xe2, 0x70, 0x7e, 0x32, 0x88, 0x76, 0xa8, 0x3a, 0xd7, 0xd4, 0x74, 0xb7, 0x9a, 0x3f, + 0x4d, 0x76, 0x5c, 0x2b, 0xde, 0x31, 0xfd, 0xb9, 0x0a, 0x8f, 0xd0, 0xe4, 0x49, 0x70, 0xf1, 0xdf, + 0xb7, 0x88, 0x2e, 0x34, 0xdf, 0x86, 0x42, 0xe2, 0x6e, 0x74, 0x5f, 0x8b, 0xe9, 0x24, 0x94, 0x7a, + 0x49, 0x28, 0x43, 0x20, 0x18, 0xc9, 0x6b, 0xee, 0x31, 0x1e, 0xbb, 0xde, 0x81, 0x96, 0x3b, 0xc2, + 0xdd, 0xc7, 0x5e, 0x13, 0xc6, 0xf5, 0xfb, 0x7b, 0x01, 0xdb, 0x68, 0xf4, 0xf9, 0xf7, 0xfd, 0xd3, + 0x21, 0x93, 0xb1, 0xd9, 0x0e, 0xac, 0x5c, 0xfa, 0x81, 0x17, 0x5e, 0x1a, 0x9b, 0x86, 0x2a, 0x6f, + 0x72, 0xf4, 0x29, 0x6c, 0x1b, 0x23, 0x47, 0xef, 0x7d, 0x91, 0x58, 0x4a, 0x69, 0x54, 0xb3, 0x1a, + 0x03, 0xe8, 0x0d, 0x38, 0xbb, 0xf0, 0xc3, 0x99, 0x48, 0x25, 0x65, 0x56, 0xbb, 0xac, 0x91, 0x6d, + 0x43, 0x83, 0xb3, 0xf1, 0x49, 0x3f, 0xba, 0x7f, 0x24, 0x54, 0x85, 0x69, 0x75, 0xa5, 0xc7, 0xf0, + 0x0b, 0xf5, 0x9a, 0x8e, 0xa1, 0xa8, 0x84, 0xad, 0x03, 0xcf, 0xd3, 0x65, 0x18, 0xf9, 0x88, 0x6d, + 0x55, 0x53, 0xb6, 0x52, 0x35, 0x5a, 0xcb, 0x74, 0x3a, 0x0b, 0x6e, 0x8f, 0x38, 0xc3, 0x4e, 0xa6, + 0x1b, 0x7a, 0x44, 0xaa, 0x15, 0x86, 0x05, 0x2f, 0x4c, 0x8f, 0x8b, 0x48, 0x55, 0x21, 0x77, 0x0f, + 0x3c, 0x2f, 0xb5, 0xcb, 0xc8, 0xf7, 0x16, 0xd4, 0x3d, 0xc6, 0xa3, 0x79, 0xeb, 0x31, 0x5e, 0xbc, + 0x33, 0x55, 0x03, 0xaa, 0x17, 0xa1, 0xcb, 0x35, 0x07, 0xbf, 0x55, 0x84, 0xbe, 0x10, 0xb3, 0xb8, + 0xa5, 0x1a, 0x4a, 0x65, 0x19, 0x7e, 0xf1, 0x93, 0xbe, 0x69, 0xa3, 0x31, 0x4d, 0x9f, 0x42, 0x27, + 0x1f, 0x88, 0xe9, 0x6e, 0xea, 0xa4, 0xfd, 0x71, 0xd4, 0x70, 0xd4, 0x49, 0x23, 0x45, 0x07, 0xb0, + 0x86, 0x19, 0x97, 0x2e, 0xa1, 0x14, 0x7e, 0x20, 0x4f, 0xe1, 0xce, 0x4c, 0xb0, 0x33, 0x3b, 0x5b, + 0x19, 0x18, 0x7d, 0xd3, 0x29, 0x5a, 0xa2, 0xaf, 0x80, 0x46, 0x13, 0x17, 0x2d, 0x17, 0xd7, 0x54, + 0xde, 0x4f, 0x07, 0x56, 0xdc, 0xd1, 0x48, 0xc6, 0x07, 0x63, 0x28, 0x3a, 0x87, 0x5f, 0x1c, 0x33, + 0x5d, 0x14, 0xcf, 0x43, 0x9e, 0xe9, 0x67, 0x89, 0x4a, 0x35, 0xad, 0x52, 0xdc, 0xc6, 0xca, 0x36, + 0x52, 0x2f, 0xdf, 0xc8, 0xdf, 0xab, 0x60, 0x1d, 0x33, 0xf9, 0x7f, 0x83, 0x0d, 0x6a, 0x9a, 0x72, + 0xf6, 0xd3, 0xcc, 0xe7, 0x26, 0x96, 0x2b, 0x9d, 0x69, 0x4d, 0x27, 0xcf, 0xa6, 0x7f, 0xab, 0xc2, + 0x46, 0x0e, 0x5b, 0xfc, 0x3a, 0x9a, 0xfd, 0xba, 0x1d, 0xdf, 0x57, 0xbd, 0x60, 0x09, 0xac, 0x40, + 0xd9, 0xff, 0x3d, 0xac, 0x78, 0x05, 0x0f, 0x0f, 0x3c, 0xaf, 0x08, 0x2a, 0xc6, 0x27, 0xf7, 0x71, + 0x36, 0xd0, 0x65, 0xd6, 0x1e, 0xc3, 0x56, 0x0e, 0x9c, 0xe2, 0xb1, 0xf9, 0x5e, 0xd4, 0x6c, 0xd4, + 0x27, 0xa5, 0x0b, 0x52, 0xf6, 0x02, 0x0c, 0xfe, 0x08, 0xda, 0x19, 0x19, 0x3b, 0x67, 0xaa, 0xae, + 0x4d, 0x5d, 0x81, 0xe5, 0x20, 0xdc, 0x28, 0xa8, 0xe5, 0x25, 0xd8, 0x88, 0x6b, 0xc0, 0x62, 0x32, + 0x57, 0x53, 0xaa, 0xa6, 0x15, 0xf4, 0x31, 0x17, 0x8c, 0xdf, 0xaa, 0x76, 0x79, 0x84, 0x41, 0x6e, + 0x61, 0xad, 0xc7, 0x34, 0xfd, 0x4b, 0x0d, 0x76, 0x9e, 0xfb, 0x81, 0x3b, 0xf1, 0xaf, 0x58, 0x21, + 0xc8, 0x2e, 0x28, 0x19, 0x03, 0xca, 0x6a, 0x19, 0x50, 0x96, 0x6a, 0x54, 0xf5, 0x4c, 0xa3, 0xc2, + 0x69, 0x22, 0x25, 0x3b, 0x9f, 0x46, 0x40, 0xad, 0xe5, 0x24, 0x0c, 0xd2, 0x87, 0x36, 0x0e, 0x41, + 0xe3, 0x74, 0x14, 0x72, 0x4f, 0x58, 0x0d, 0xbc, 0xa4, 0x8e, 0xbe, 0xa4, 0xb3, 0xdc, 0xb2, 0xb3, + 0xa8, 0x40, 0x9e, 0xc1, 0x66, 0xc2, 0x3c, 0xe2, 0x3c, 0xe4, 0x08, 0xe4, 0x56, 0xed, 0x6d, 0x6d, + 0x63, 0xc0, 0xc3, 0x1f, 0x26, 0xec, 0xbc, 0xcf, 0xa4, 0xeb, 0x4f, 0x84, 0x93, 0x17, 0xa6, 0xef, + 0x61, 0xfb, 0xc0, 0xf3, 0x0e, 0x27, 0xe1, 0xe8, 0x1d, 0xf3, 0x5e, 0xb2, 0x79, 0x6a, 0xd0, 0xbc, + 0x63, 0xf3, 0x17, 0xae, 0x78, 0x6b, 0xda, 0x69, 0x44, 0xaa, 0x7a, 0x77, 0x3d, 0x8f, 0x79, 0x51, + 0x4b, 0x45, 0x02, 0x4f, 0x27, 0x9c, 0xf1, 0x11, 0x8b, 0x21, 0x2b, 0x52, 0xd8, 0xe0, 0xc3, 0x73, + 0x95, 0xef, 0xe6, 0x04, 0x22, 0x92, 0xfe, 0x0a, 0xda, 0x2f, 0xd9, 0xdc, 0x78, 0xbe, 0xd6, 0xad, + 0xfd, 0xf3, 0x1d, 0xd8, 0x1a, 0xca, 0x90, 0xbb, 0xe3, 0xe8, 0xba, 0xe4, 0x9c, 0xec, 0xc3, 0xe6, + 0x31, 0xcb, 0x00, 0x14, 0x42, 0x70, 0x2a, 0x67, 0xba, 0x42, 0x97, 0xe8, 0xb3, 0x48, 0x73, 0x69, + 0x85, 0xfc, 0x06, 0xb6, 0x73, 0xca, 0x87, 0x73, 0xf5, 0x6a, 0xdb, 0x50, 0x16, 0x92, 0x57, 0x5c, + 0x89, 0xf6, 0xa7, 0xb0, 0x71, 0xcc, 0xd2, 0xf8, 0x8f, 0x80, 0xd2, 0xd3, 0xc3, 0xb0, 0xdb, 0xd6, + 0x3a, 0xa9, 0x65, 0x5a, 0x21, 0x9f, 0x41, 0x5b, 0x3d, 0xec, 0x38, 0x1b, 0xdd, 0x44, 0x6b, 0x1f, + 0xc3, 0x5c, 0x7c, 0x3c, 0xa4, 0x15, 0xef, 0x22, 0x1a, 0xcc, 0x8b, 0xd0, 0x0a, 0x19, 0x82, 0x55, + 0x86, 0x53, 0xc9, 0x87, 0x31, 0x84, 0x2c, 0x47, 0xb1, 0xdd, 0xad, 0x3c, 0xce, 0xa4, 0x15, 0xf2, + 0x02, 0x3a, 0xc5, 0xc0, 0x90, 0x3c, 0x8a, 0xa5, 0xcb, 0x40, 0x63, 0xb7, 0x15, 0x8b, 0xd0, 0x0a, + 0xf9, 0x0e, 0xee, 0x95, 0x48, 0x23, 0x42, 0xbe, 0xa9, 0x39, 0x1b, 0x56, 0x53, 0xa0, 0x8e, 0x74, + 0xe2, 0xb5, 0x0c, 0xca, 0xcb, 0xea, 0x7c, 0x0e, 0xeb, 0x19, 0xcc, 0x46, 0xac, 0x78, 0x35, 0x07, + 0xe3, 0xb2, 0x7a, 0x5f, 0xc0, 0x7a, 0x06, 0xa1, 0x69, 0xbd, 0x22, 0xd0, 0xd6, 0xc5, 0x9b, 0xd2, + 0x2c, 0x5a, 0x21, 0xaf, 0xe1, 0x83, 0x52, 0xa0, 0x46, 0x1e, 0x2b, 0xd1, 0xeb, 0x70, 0x5c, 0xce, + 0xe0, 0xd7, 0x98, 0x56, 0xd9, 0x7e, 0x4b, 0xb6, 0x17, 0x06, 0xd2, 0x49, 0xdf, 0xee, 0x16, 0x75, + 0x7f, 0xbc, 0x50, 0xb2, 0x30, 0x79, 0x6d, 0xb2, 0xa3, 0x4c, 0x94, 0x4d, 0xe4, 0x2e, 0x59, 0x9c, + 0x78, 0xb4, 0x42, 0xde, 0xe0, 0x0c, 0x2f, 0x1a, 0x48, 0x36, 0xa1, 0xc6, 0xde, 0x92, 0x3f, 0x1b, + 0x65, 0x01, 0x3e, 0x33, 0x79, 0x52, 0x38, 0xe9, 0xec, 0xc2, 0x9a, 0xcf, 0x5c, 0xd6, 0x9f, 0x60, + 0x67, 0x09, 0x48, 0xb2, 0xc9, 0x13, 0x13, 0xda, 0x35, 0x30, 0xaa, 0x64, 0xd3, 0xdf, 0x9b, 0xe8, + 0x0a, 0x5f, 0x35, 0x36, 0xf9, 0x28, 0x8e, 0x64, 0xd9, 0xb3, 0x27, 0x1b, 0xb0, 0x83, 0x38, 0xec, + 0xac, 0xc8, 0xdc, 0xa3, 0x74, 0xac, 0x37, 0x09, 0xf3, 0x53, 0x80, 0xa4, 0xe1, 0x12, 0x6c, 0x19, + 0x0b, 0x0d, 0x38, 0x97, 0x5a, 0xfb, 0xb0, 0x79, 0xca, 0x2e, 0x73, 0xfd, 0x75, 0xa1, 0x1b, 0x96, + 0x74, 0xc8, 0x2f, 0x80, 0xe8, 0xdf, 0x11, 0xd7, 0xea, 0xaf, 0x6a, 0xde, 0xd1, 0xf9, 0x54, 0xce, + 0x69, 0x85, 0x9c, 0xc0, 0x46, 0x16, 0x56, 0x93, 0x0f, 0x70, 0x43, 0x45, 0x98, 0xbf, 0xdb, 0x2d, + 0x5a, 0x32, 0x33, 0xbe, 0x42, 0x7e, 0x0b, 0x6d, 0x05, 0x90, 0xb2, 0x2d, 0x77, 0x89, 0xb5, 0x5c, + 0x24, 0x4f, 0xa1, 0x15, 0x3f, 0x70, 0x4c, 0x49, 0xe5, 0xde, 0x3b, 0x79, 0x8d, 0x7d, 0xe8, 0xf4, + 0x99, 0x3b, 0x92, 0xfe, 0xc5, 0xe2, 0xc6, 0x17, 0x93, 0x34, 0xa7, 0xfc, 0x04, 0x9a, 0xa7, 0xec, + 0x12, 0xf3, 0x8f, 0x98, 0x25, 0x24, 0xba, 0x69, 0x02, 0xc3, 0x22, 0x43, 0x83, 0xd2, 0x07, 0x3c, + 0x1c, 0x31, 0x21, 0xfc, 0x60, 0x5c, 0xa8, 0x11, 0x59, 0xfe, 0x25, 0xac, 0x47, 0x1a, 0x38, 0xf7, + 0xaf, 0x13, 0x8e, 0x90, 0x51, 0x79, 0x2c, 0x89, 0x70, 0x33, 0x7a, 0x31, 0x10, 0x1c, 0x16, 0xe9, + 0xf7, 0x4d, 0x3e, 0xf0, 0x67, 0xb0, 0x95, 0x7f, 0x5e, 0x90, 0x7b, 0x26, 0x9f, 0x8b, 0x1e, 0x1d, + 0x79, 0xfd, 0xaf, 0xa1, 0xbd, 0x00, 0x18, 0x75, 0x9f, 0x2a, 0xc3, 0x91, 0xf9, 0x70, 0x1d, 0x20, + 0xa7, 0xec, 0x32, 0x5f, 0x53, 0x1f, 0x9a, 0xab, 0x5d, 0x86, 0xa4, 0xf5, 0x90, 0x5d, 0x80, 0xb5, + 0x98, 0xaf, 0x9d, 0x42, 0x24, 0x69, 0x93, 0x1e, 0xce, 0x84, 0x25, 0x28, 0x33, 0x1f, 0xde, 0x57, + 0x60, 0x25, 0xe9, 0xf3, 0x1f, 0xb5, 0xf4, 0x9c, 0x81, 0x5d, 0x58, 0xd3, 0xf9, 0x69, 0x06, 0x4a, + 0x1a, 0x25, 0x64, 0x6b, 0xfb, 0x4b, 0x58, 0xcf, 0x20, 0x3f, 0x3d, 0xc0, 0x8a, 0xc0, 0x60, 0xce, + 0xc7, 0xe1, 0xed, 0x3f, 0x36, 0xf0, 0x7f, 0xf8, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0x26, 0xa2, + 0x44, 0x9d, 0x3e, 0x17, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -1992,6 +2102,7 @@ type StorageAuthorityClient interface { GetValidOrderAuthorizations2(ctx context.Context, in *GetValidOrderAuthorizationsRequest, opts ...grpc.CallOption) (*Authorizations, error) CountInvalidAuthorizations2(ctx context.Context, in *CountInvalidAuthorizationsRequest, opts ...grpc.CallOption) (*Count, error) GetValidAuthorizations2(ctx context.Context, in *GetValidAuthorizationsRequest, opts ...grpc.CallOption) (*Authorizations, error) + KeyBlocked(ctx context.Context, in *KeyBlockedRequest, opts ...grpc.CallOption) (*Exists, error) // Adders NewRegistration(ctx context.Context, in *proto1.Registration, opts ...grpc.CallOption) (*proto1.Registration, error) UpdateRegistration(ctx context.Context, in *proto1.Registration, opts ...grpc.CallOption) (*proto1.Empty, error) @@ -2010,6 +2121,7 @@ type StorageAuthorityClient interface { FinalizeAuthorization2(ctx context.Context, in *FinalizeAuthorizationRequest, opts ...grpc.CallOption) (*proto1.Empty, error) DeactivateAuthorization2(ctx context.Context, in *AuthorizationID2, opts ...grpc.CallOption) (*proto1.Empty, error) SerialExists(ctx context.Context, in *Serial, opts ...grpc.CallOption) (*Exists, error) + AddBlockedKey(ctx context.Context, in *AddBlockedKeyRequest, opts ...grpc.CallOption) (*proto1.Empty, error) } type storageAuthorityClient struct { @@ -2191,6 +2303,15 @@ func (c *storageAuthorityClient) GetValidAuthorizations2(ctx context.Context, in return out, nil } +func (c *storageAuthorityClient) KeyBlocked(ctx context.Context, in *KeyBlockedRequest, opts ...grpc.CallOption) (*Exists, error) { + out := new(Exists) + err := c.cc.Invoke(ctx, "/sa.StorageAuthority/KeyBlocked", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *storageAuthorityClient) NewRegistration(ctx context.Context, in *proto1.Registration, opts ...grpc.CallOption) (*proto1.Registration, error) { out := new(proto1.Registration) err := c.cc.Invoke(ctx, "/sa.StorageAuthority/NewRegistration", in, out, opts...) @@ -2344,6 +2465,15 @@ func (c *storageAuthorityClient) SerialExists(ctx context.Context, in *Serial, o return out, nil } +func (c *storageAuthorityClient) AddBlockedKey(ctx context.Context, in *AddBlockedKeyRequest, opts ...grpc.CallOption) (*proto1.Empty, error) { + out := new(proto1.Empty) + err := c.cc.Invoke(ctx, "/sa.StorageAuthority/AddBlockedKey", in, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // StorageAuthorityServer is the server API for StorageAuthority service. type StorageAuthorityServer interface { // Getters @@ -2368,6 +2498,7 @@ type StorageAuthorityServer interface { GetValidOrderAuthorizations2(context.Context, *GetValidOrderAuthorizationsRequest) (*Authorizations, error) CountInvalidAuthorizations2(context.Context, *CountInvalidAuthorizationsRequest) (*Count, error) GetValidAuthorizations2(context.Context, *GetValidAuthorizationsRequest) (*Authorizations, error) + KeyBlocked(context.Context, *KeyBlockedRequest) (*Exists, error) // Adders NewRegistration(context.Context, *proto1.Registration) (*proto1.Registration, error) UpdateRegistration(context.Context, *proto1.Registration) (*proto1.Empty, error) @@ -2386,6 +2517,7 @@ type StorageAuthorityServer interface { FinalizeAuthorization2(context.Context, *FinalizeAuthorizationRequest) (*proto1.Empty, error) DeactivateAuthorization2(context.Context, *AuthorizationID2) (*proto1.Empty, error) SerialExists(context.Context, *Serial) (*Exists, error) + AddBlockedKey(context.Context, *AddBlockedKeyRequest) (*proto1.Empty, error) } // UnimplementedStorageAuthorityServer can be embedded to have forward compatible implementations. @@ -2449,6 +2581,9 @@ func (*UnimplementedStorageAuthorityServer) CountInvalidAuthorizations2(ctx cont func (*UnimplementedStorageAuthorityServer) GetValidAuthorizations2(ctx context.Context, req *GetValidAuthorizationsRequest) (*Authorizations, error) { return nil, status.Errorf(codes.Unimplemented, "method GetValidAuthorizations2 not implemented") } +func (*UnimplementedStorageAuthorityServer) KeyBlocked(ctx context.Context, req *KeyBlockedRequest) (*Exists, error) { + return nil, status.Errorf(codes.Unimplemented, "method KeyBlocked not implemented") +} func (*UnimplementedStorageAuthorityServer) NewRegistration(ctx context.Context, req *proto1.Registration) (*proto1.Registration, error) { return nil, status.Errorf(codes.Unimplemented, "method NewRegistration not implemented") } @@ -2500,6 +2635,9 @@ func (*UnimplementedStorageAuthorityServer) DeactivateAuthorization2(ctx context func (*UnimplementedStorageAuthorityServer) SerialExists(ctx context.Context, req *Serial) (*Exists, error) { return nil, status.Errorf(codes.Unimplemented, "method SerialExists not implemented") } +func (*UnimplementedStorageAuthorityServer) AddBlockedKey(ctx context.Context, req *AddBlockedKeyRequest) (*proto1.Empty, error) { + return nil, status.Errorf(codes.Unimplemented, "method AddBlockedKey not implemented") +} func RegisterStorageAuthorityServer(s *grpc.Server, srv StorageAuthorityServer) { s.RegisterService(&_StorageAuthority_serviceDesc, srv) @@ -2847,6 +2985,24 @@ func _StorageAuthority_GetValidAuthorizations2_Handler(srv interface{}, ctx cont return interceptor(ctx, in, info, handler) } +func _StorageAuthority_KeyBlocked_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(KeyBlockedRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StorageAuthorityServer).KeyBlocked(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/sa.StorageAuthority/KeyBlocked", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StorageAuthorityServer).KeyBlocked(ctx, req.(*KeyBlockedRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _StorageAuthority_NewRegistration_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(proto1.Registration) if err := dec(in); err != nil { @@ -3153,6 +3309,24 @@ func _StorageAuthority_SerialExists_Handler(srv interface{}, ctx context.Context return interceptor(ctx, in, info, handler) } +func _StorageAuthority_AddBlockedKey_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(AddBlockedKeyRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(StorageAuthorityServer).AddBlockedKey(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: "/sa.StorageAuthority/AddBlockedKey", + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(StorageAuthorityServer).AddBlockedKey(ctx, req.(*AddBlockedKeyRequest)) + } + return interceptor(ctx, in, info, handler) +} + var _StorageAuthority_serviceDesc = grpc.ServiceDesc{ ServiceName: "sa.StorageAuthority", HandlerType: (*StorageAuthorityServer)(nil), @@ -3233,6 +3407,10 @@ var _StorageAuthority_serviceDesc = grpc.ServiceDesc{ MethodName: "GetValidAuthorizations2", Handler: _StorageAuthority_GetValidAuthorizations2_Handler, }, + { + MethodName: "KeyBlocked", + Handler: _StorageAuthority_KeyBlocked_Handler, + }, { MethodName: "NewRegistration", Handler: _StorageAuthority_NewRegistration_Handler, @@ -3301,6 +3479,10 @@ var _StorageAuthority_serviceDesc = grpc.ServiceDesc{ MethodName: "SerialExists", Handler: _StorageAuthority_SerialExists_Handler, }, + { + MethodName: "AddBlockedKey", + Handler: _StorageAuthority_AddBlockedKey_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "sa/proto/sa.proto", diff --git a/sa/proto/sa.proto b/sa/proto/sa.proto index d5a7736eb8a..bf8aaf2fabd 100644 --- a/sa/proto/sa.proto +++ b/sa/proto/sa.proto @@ -28,6 +28,7 @@ service StorageAuthority { rpc GetValidOrderAuthorizations2(GetValidOrderAuthorizationsRequest) returns (Authorizations) {} rpc CountInvalidAuthorizations2(CountInvalidAuthorizationsRequest) returns (Count) {} rpc GetValidAuthorizations2(GetValidAuthorizationsRequest) returns (Authorizations) {} + rpc KeyBlocked(KeyBlockedRequest) returns (Exists) {} // Adders rpc NewRegistration(core.Registration) returns (core.Registration) {} rpc UpdateRegistration(core.Registration) returns (core.Empty) {} @@ -46,6 +47,7 @@ service StorageAuthority { rpc FinalizeAuthorization2(FinalizeAuthorizationRequest) returns (core.Empty) {} rpc DeactivateAuthorization2(AuthorizationID2) returns (core.Empty) {} rpc SerialExists(Serial) returns (Exists) {} + rpc AddBlockedKey(AddBlockedKeyRequest) returns (core.Empty) {} } message RegistrationID { @@ -242,3 +244,14 @@ message FinalizeAuthorizationRequest { repeated core.ValidationRecord validationRecords = 5; optional core.ProblemDetails validationError = 6; } + +message AddBlockedKeyRequest { + optional bytes keyHash = 1; + optional int64 added = 2; // Unix timestamp (nanoseconds) + optional string source = 3; + optional string comment = 4; +} + +message KeyBlockedRequest { + optional bytes keyHash = 1; +} diff --git a/sa/sa.go b/sa/sa.go index 20c8f536d90..8079ab187ef 100644 --- a/sa/sa.go +++ b/sa/sa.go @@ -1771,3 +1771,45 @@ func addKeyHash(db db.Inserter, cert *x509.Certificate) error { } return db.Insert(khm) } + +// AddBlockedKey adds a key hash to the blockedKeys table +func (ssa *SQLStorageAuthority) AddBlockedKey(ctx context.Context, req *sapb.AddBlockedKeyRequest) (*corepb.Empty, error) { + if req == nil || req.KeyHash == nil || req.Added == nil || req.Source == nil { + return nil, errIncompleteRequest + } + sourceInt, ok := stringToSourceInt[*req.Source] + if !ok { + return nil, errors.New("unknown source") + } + err := ssa.dbMap.Insert(&blockedKeyModel{ + KeyHash: req.KeyHash, + Added: time.Unix(0, *req.Added), + Source: sourceInt, + Comment: req.Comment, + }) + if err != nil { + if db.IsDuplicate(err) { + // Ignore duplicate inserts so multiple certs with the same key can + // be revoked. + return &corepb.Empty{}, nil + } + return nil, err + } + return &corepb.Empty{}, nil +} + +// KeyBlocked checks if a key, indicated by a hash, is present in the blockedKeys table +func (ssa *SQLStorageAuthority) KeyBlocked(ctx context.Context, req *sapb.KeyBlockedRequest) (*sapb.Exists, error) { + if req == nil || req.KeyHash == nil { + return nil, errIncompleteRequest + } + exists := false + if err := ssa.dbMap.SelectOne(&blockedKeyModel{}, `SELECT * FROM blockedKeys WHERE keyHash = ?`, req.KeyHash); err != nil { + if db.IsNoRows(err) { + return &sapb.Exists{Exists: &exists}, nil + } + return nil, err + } + exists = true + return &sapb.Exists{Exists: &exists}, nil +} diff --git a/sa/sa_test.go b/sa/sa_test.go index e2c02f0555f..02d41849aa3 100644 --- a/sa/sa_test.go +++ b/sa/sa_test.go @@ -12,7 +12,9 @@ import ( "math/big" "math/bits" "net" + "os" "reflect" + "strings" "sync" "testing" "time" @@ -2247,3 +2249,75 @@ func TestSerialExists(t *testing.T) { test.AssertNotError(t, err, "SerialExists failed") test.AssertEquals(t, *resp.Exists, true) } + +func TestBlockedKey(t *testing.T) { + if !strings.HasSuffix(os.Getenv("BOULDER_CONFIG_DIR"), "config-next") { + return + } + + sa, _, cleanUp := initSA(t) + defer cleanUp() + + hashA := make([]byte, 32) + hashA[0] = 1 + hashB := make([]byte, 32) + hashB[0] = 2 + + added := time.Now().UnixNano() + source := "API" + _, err := sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ + KeyHash: hashA, + Added: &added, + Source: &source, + }) + test.AssertNotError(t, err, "AddBlockedKey failed") + _, err = sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ + KeyHash: hashA, + Added: &added, + Source: &source, + }) + test.AssertNotError(t, err, "AddBlockedKey failed with duplicate insert") + + comment := "testing comments" + _, err = sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ + KeyHash: hashB, + Added: &added, + Source: &source, + Comment: &comment, + }) + test.AssertNotError(t, err, "AddBlockedKey failed") + + exists, err := sa.KeyBlocked(context.Background(), &sapb.KeyBlockedRequest{ + KeyHash: hashA, + }) + test.AssertNotError(t, err, "KeyBlocked failed") + test.Assert(t, exists != nil, "*sapb.Exists is nil") + test.Assert(t, *exists.Exists, "KeyBlocked returned false for blocked key") + exists, err = sa.KeyBlocked(context.Background(), &sapb.KeyBlockedRequest{ + KeyHash: hashB, + }) + test.AssertNotError(t, err, "KeyBlocked failed") + test.Assert(t, exists != nil, "*sapb.Exists is nil") + test.Assert(t, *exists.Exists, "KeyBlocked returned false for blocked key") + exists, err = sa.KeyBlocked(context.Background(), &sapb.KeyBlockedRequest{ + KeyHash: []byte{5}, + }) + test.AssertNotError(t, err, "KeyBlocked failed") + test.Assert(t, exists != nil, "*sapb.Exists is nil") + test.Assert(t, !*exists.Exists, "KeyBlocked returned true for non-blocked key") +} + +func TestAddBlockedKeyUnknownSource(t *testing.T) { + sa, _, cleanUp := initSA(t) + defer cleanUp() + + added := int64(0) + source := "heyo" + _, err := sa.AddBlockedKey(context.Background(), &sapb.AddBlockedKeyRequest{ + KeyHash: []byte{1, 2, 3}, + Added: &added, + Source: &source, + }) + test.AssertError(t, err, "AddBlockedKey didn't fail with unknown source") + test.AssertEquals(t, err.Error(), "unknown source") +} diff --git a/test/config-next/ca-a.json b/test/config-next/ca-a.json index c2003ab7d28..13bee5cd3fa 100644 --- a/test/config-next/ca-a.json +++ b/test/config-next/ca-a.json @@ -137,7 +137,8 @@ "maxConcurrentRPCServerRequests": 100000, "orphanQueueDir": "/tmp/orphaned-certificates-a", "features": { - "StoreIssuerInfo": true + "StoreIssuerInfo": true, + "BlockedKeyTable": true } }, diff --git a/test/config-next/ca-b.json b/test/config-next/ca-b.json index 086122e654b..133756dc57f 100644 --- a/test/config-next/ca-b.json +++ b/test/config-next/ca-b.json @@ -138,7 +138,8 @@ "maxConcurrentRPCServerRequests": 100000, "orphanQueueDir": "/tmp/orphaned-certificates-b", "features": { - "StoreIssuerInfo": true + "StoreIssuerInfo": true, + "BlockedKeyTable": true } }, diff --git a/test/config-next/ra.json b/test/config-next/ra.json index e98316a2efd..b718e7303e1 100644 --- a/test/config-next/ra.json +++ b/test/config-next/ra.json @@ -46,6 +46,7 @@ ] }, "features": { + "BlockedKeyTable": true }, "CTLogGroups2": [ { diff --git a/test/config-next/wfe.json b/test/config-next/wfe.json index 5033a61c697..d643adc9adb 100644 --- a/test/config-next/wfe.json +++ b/test/config-next/wfe.json @@ -39,7 +39,8 @@ } }, "features": { - "StripDefaultSchemePort": true + "StripDefaultSchemePort": true, + "BlockedKeyTable": true } }, diff --git a/test/config-next/wfe2.json b/test/config-next/wfe2.json index 66bac96525f..11f2d955df1 100644 --- a/test/config-next/wfe2.json +++ b/test/config-next/wfe2.json @@ -53,7 +53,8 @@ "features": { "MandatoryPOSTAsGET": true, "PrecertificateRevocation": true, - "StripDefaultSchemePort": true + "StripDefaultSchemePort": true, + "BlockedKeyTable": true } }, diff --git a/test/integration/revocation_test.go b/test/integration/revocation_test.go index 68bce2d1a7a..ffa5c863464 100644 --- a/test/integration/revocation_test.go +++ b/test/integration/revocation_test.go @@ -127,7 +127,7 @@ func TestPrecertificateRevocation(t *testing.T) { tc.revokeClient.Account, cert, tc.revokeKey, - ocsp.KeyCompromise) + ocsp.Unspecified) test.AssertNotError(t, err, "revoking precert") // Check the OCSP response for the precertificate again. It should now be @@ -137,3 +137,33 @@ func TestPrecertificateRevocation(t *testing.T) { }) } } + +func TestRevokeWithKeyCompromise(t *testing.T) { + t.Parallel() + if !strings.HasSuffix(os.Getenv("BOULDER_CONFIG_DIR"), "config-next") { + return + } + + os.Setenv("DIRECTORY", "http://boulder:4001/directory") + c, err := makeClient("mailto:example@letsencrypt.org") + test.AssertNotError(t, err, "creating acme client") + + certKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + test.AssertNotError(t, err, "failed to generate cert key") + + res, err := authAndIssue(c, certKey, []string{random_domain()}) + test.AssertNotError(t, err, "authAndIssue failed") + + err = c.RevokeCertificate( + c.Account, + res.certs[0], + c.Account.PrivateKey, + ocsp.KeyCompromise, + ) + test.AssertNotError(t, err, "failed to revoke certificate") + + // attempt to create a new account using the blacklisted key + _, err = c.NewAccount(certKey, false, true) + test.AssertError(t, err, "NewAccount didn't fail with a blacklisted key") + test.AssertEquals(t, err.Error(), `acme: error code 400 "urn:ietf:params:acme:error:badPublicKey": public key is forbidden`) +} diff --git a/test/sa_db_users.sql b/test/sa_db_users.sql index 536e000d823..c74053b51d8 100644 --- a/test/sa_db_users.sql +++ b/test/sa_db_users.sql @@ -30,6 +30,7 @@ GRANT SELECT,INSERT ON orderToAuthz2 TO 'sa'@'localhost'; GRANT INSERT,SELECT ON serials TO 'sa'@'localhost'; GRANT SELECT,INSERT ON precertificates TO 'sa'@'localhost'; GRANT SELECT,INSERT ON keyHashToSerial TO 'sa'@'localhost'; +GRANT SELECT,INSERT ON blockedKeys TO 'sa'@'localhost'; -- OCSP Responder GRANT SELECT ON certificateStatus TO 'ocsp_resp'@'localhost'; diff --git a/wfe/wfe.go b/wfe/wfe.go index 4620234d29c..8f702e79b2a 100644 --- a/wfe/wfe.go +++ b/wfe/wfe.go @@ -528,7 +528,7 @@ func (wfe *WebFrontEndImpl) verifyPOST(ctx context.Context, logEvent *web.Reques // When looking up keys from the registrations DB, we can be confident they // are "good". But when we are verifying against any submitted key, we want // to check its quality before doing the verify. - if err = wfe.keyPolicy.GoodKey(submittedKey.Key); err != nil { + if err = wfe.keyPolicy.GoodKey(ctx, submittedKey.Key); err != nil { wfe.joseErrorCounter.WithLabelValues("JWKRejectedByGoodKey").Inc() return nil, nil, reg, probs.Malformed(err.Error()) } @@ -959,7 +959,7 @@ func (wfe *WebFrontEndImpl) NewCertificate(ctx context.Context, logEvent *web.Re // bytes on the wire, and (b) the CA logs all rejections as audit events, but // a bad key from the client is just a malformed request and doesn't need to // be audited. - if err := wfe.keyPolicy.GoodKey(certificateRequest.CSR.PublicKey); err != nil { + if err := wfe.keyPolicy.GoodKey(ctx, certificateRequest.CSR.PublicKey); err != nil { wfe.sendError(response, logEvent, probs.Malformed("Invalid key in certificate request :: %s", err), err) return } diff --git a/wfe2/verify.go b/wfe2/verify.go index 609a11fd877..b823b5d3f18 100644 --- a/wfe2/verify.go +++ b/wfe2/verify.go @@ -626,7 +626,7 @@ func (wfe *WebFrontEndImpl) validSelfAuthenticatedJWS( } // If the key doesn't meet the GoodKey policy return a problem immediately - if err := wfe.keyPolicy.GoodKey(pubKey.Key); err != nil { + if err := wfe.keyPolicy.GoodKey(ctx, pubKey.Key); err != nil { wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "JWKRejectedByGoodKey"}).Inc() return nil, nil, probs.BadPublicKey(err.Error()) } @@ -689,6 +689,7 @@ type rolloverOperation struct { // account) and that the account field of the rollover object matches the // account that verified the outer JWS. func (wfe *WebFrontEndImpl) validKeyRollover( + ctx context.Context, outerJWS *jose.JSONWebSignature, innerJWS *jose.JSONWebSignature, oldKey *jose.JSONWebKey, @@ -701,7 +702,7 @@ func (wfe *WebFrontEndImpl) validKeyRollover( } // If the key doesn't meet the GoodKey policy return a problem immediately - if err := wfe.keyPolicy.GoodKey(jwk.Key); err != nil { + if err := wfe.keyPolicy.GoodKey(ctx, jwk.Key); err != nil { wfe.stats.joseErrorCount.With(prometheus.Labels{"type": "KeyRolloverJWKRejectedByGoodKey"}).Inc() return nil, probs.BadPublicKey(err.Error()) } diff --git a/wfe2/wfe.go b/wfe2/wfe.go index 48a68605d90..59f01539f35 100644 --- a/wfe2/wfe.go +++ b/wfe2/wfe.go @@ -1789,7 +1789,7 @@ func (wfe *WebFrontEndImpl) KeyRollover( } // Validate the inner JWS as a key rollover request for the outer JWS - rolloverOperation, prob := wfe.validKeyRollover(outerJWS, innerJWS, oldKey, logEvent) + rolloverOperation, prob := wfe.validKeyRollover(ctx, outerJWS, innerJWS, oldKey, logEvent) if prob != nil { wfe.sendError(response, logEvent, prob, nil) return