diff --git a/docs/grpc/index.html b/docs/grpc/index.html index c598fdd7a2..ac4fd9dc06 100644 --- a/docs/grpc/index.html +++ b/docs/grpc/index.html @@ -4043,6 +4043,24 @@

Algorithm

+ + ALGORITHM_MLKEM_768 + 20 +

+ + + + ALGORITHM_MLKEM_1024 + 21 +

+ + + + ALGORITHM_XWING + 30 +

+ + @@ -4154,6 +4172,24 @@

KasPublicKeyAlgEnum

+ + KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + 20 +

+ + + + KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + 21 +

+ + + + KAS_PUBLIC_KEY_ALG_ENUM_XWING + 30 +

+ + diff --git a/docs/openapi/authorization/authorization.openapi.yaml b/docs/openapi/authorization/authorization.openapi.yaml index b17083ebd1..d1c9d25346 100644 --- a/docs/openapi/authorization/authorization.openapi.yaml +++ b/docs/openapi/authorization/authorization.openapi.yaml @@ -343,6 +343,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.KasPublicKeyAlgEnum: type: string @@ -354,6 +357,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.SourceType: type: string title: SourceType diff --git a/docs/openapi/authorization/v2/authorization.openapi.yaml b/docs/openapi/authorization/v2/authorization.openapi.yaml index 0f4f92d55f..c0ccd4151d 100644 --- a/docs/openapi/authorization/v2/authorization.openapi.yaml +++ b/docs/openapi/authorization/v2/authorization.openapi.yaml @@ -175,6 +175,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.KasPublicKeyAlgEnum: type: string @@ -186,6 +189,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.SourceType: type: string title: SourceType diff --git a/docs/openapi/policy/actions/actions.openapi.yaml b/docs/openapi/policy/actions/actions.openapi.yaml index ec633192ae..c75968d8bb 100644 --- a/docs/openapi/policy/actions/actions.openapi.yaml +++ b/docs/openapi/policy/actions/actions.openapi.yaml @@ -203,6 +203,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -229,6 +232,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.SourceType: type: string title: SourceType diff --git a/docs/openapi/policy/attributes/attributes.openapi.yaml b/docs/openapi/policy/attributes/attributes.openapi.yaml index 0fcb66f710..0dd745cedb 100644 --- a/docs/openapi/policy/attributes/attributes.openapi.yaml +++ b/docs/openapi/policy/attributes/attributes.openapi.yaml @@ -724,6 +724,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -750,6 +753,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.SortDirection: type: string title: SortDirection diff --git a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml index 9cdc346918..cdf0b6a95a 100644 --- a/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml +++ b/docs/openapi/policy/kasregistry/key_access_server_registry.openapi.yaml @@ -534,6 +534,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.KasPublicKeyAlgEnum: type: string @@ -545,6 +548,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.KeyMode: type: string title: KeyMode @@ -1147,7 +1153,7 @@ components: Required The algorithm to be used for the key The key_algorithm must be one of the defined values.: ``` - this in [1, 2, 3, 4, 5] + this in [1, 2, 3, 4, 5, 20, 21] ``` $ref: '#/components/schemas/policy.Algorithm' @@ -1932,7 +1938,7 @@ components: Required The key_algorithm must be one of the defined values.: ``` - this in [1, 2, 3, 4, 5] + this in [1, 2, 3, 4, 5, 20, 21] ``` $ref: '#/components/schemas/policy.Algorithm' diff --git a/docs/openapi/policy/namespaces/namespaces.openapi.yaml b/docs/openapi/policy/namespaces/namespaces.openapi.yaml index 4a5b7e35cb..7db6260890 100644 --- a/docs/openapi/policy/namespaces/namespaces.openapi.yaml +++ b/docs/openapi/policy/namespaces/namespaces.openapi.yaml @@ -353,6 +353,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.KasPublicKeyAlgEnum: type: string @@ -364,6 +367,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.SortDirection: type: string title: SortDirection diff --git a/docs/openapi/policy/objects.openapi.yaml b/docs/openapi/policy/objects.openapi.yaml index 917017b1a0..a67d2a1d8a 100644 --- a/docs/openapi/policy/objects.openapi.yaml +++ b/docs/openapi/policy/objects.openapi.yaml @@ -21,6 +21,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -47,6 +50,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.KeyMode: type: string title: KeyMode diff --git a/docs/openapi/policy/obligations/obligations.openapi.yaml b/docs/openapi/policy/obligations/obligations.openapi.yaml index dbe62f43ca..7cfbbf3e72 100644 --- a/docs/openapi/policy/obligations/obligations.openapi.yaml +++ b/docs/openapi/policy/obligations/obligations.openapi.yaml @@ -518,6 +518,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -544,6 +547,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.SourceType: type: string title: SourceType diff --git a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml index 97b2d07814..d30a4cdac1 100644 --- a/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml +++ b/docs/openapi/policy/registeredresources/registered_resources.openapi.yaml @@ -413,6 +413,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -439,6 +442,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.SourceType: type: string title: SourceType diff --git a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml index c768b805c0..b1c0cf254e 100644 --- a/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml +++ b/docs/openapi/policy/resourcemapping/resource_mapping.openapi.yaml @@ -413,6 +413,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -439,6 +442,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.SourceType: type: string title: SourceType diff --git a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml index ce818161fb..2350ded2e0 100644 --- a/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml +++ b/docs/openapi/policy/subjectmapping/subject_mapping.openapi.yaml @@ -449,6 +449,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -475,6 +478,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.SortDirection: type: string title: SortDirection diff --git a/docs/openapi/policy/unsafe/unsafe.openapi.yaml b/docs/openapi/policy/unsafe/unsafe.openapi.yaml index f52e95b427..be8ff0592c 100644 --- a/docs/openapi/policy/unsafe/unsafe.openapi.yaml +++ b/docs/openapi/policy/unsafe/unsafe.openapi.yaml @@ -387,6 +387,9 @@ components: - ALGORITHM_EC_P256 - ALGORITHM_EC_P384 - ALGORITHM_EC_P521 + - ALGORITHM_MLKEM_768 + - ALGORITHM_MLKEM_1024 + - ALGORITHM_XWING description: Supported key algorithms. policy.AttributeRuleTypeEnum: type: string @@ -413,6 +416,9 @@ components: - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 - KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + - KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + - KAS_PUBLIC_KEY_ALG_ENUM_XWING policy.KeyMode: type: string title: KeyMode diff --git a/lib/ocrypto/asym_decryption.go b/lib/ocrypto/asym_decryption.go index 426f859723..d98f55ccd8 100644 --- a/lib/ocrypto/asym_decryption.go +++ b/lib/ocrypto/asym_decryption.go @@ -7,6 +7,7 @@ import ( "crypto/ecdh" "crypto/ecdsa" "crypto/elliptic" + "crypto/mlkem" "crypto/rsa" "crypto/sha256" "crypto/x509" @@ -26,6 +27,21 @@ type AsymDecryption struct { type PrivateKeyDecryptor interface { // Decrypt decrypts ciphertext with private key. Decrypt(data []byte) ([]byte, error) + + // DecryptWithEphemeralKey decrypts ciphertext using additional sender material. + DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) +} + +type MLKEMDecryptor768 struct { + decap *mlkem.DecapsulationKey768 +} + +type MLKEMDecryptor1024 struct { + decap *mlkem.DecapsulationKey1024 +} + +type XWingDecryptor struct { + seed [xwingSeedSize]byte } // FromPrivatePEM creates and returns a new AsymDecryption. @@ -44,6 +60,25 @@ func FromPrivatePEMWithSalt(privateKeyInPem string, salt, info []byte) (PrivateK return AsymDecryption{}, errors.New("failed to parse PEM formatted private key") } + // Try X-Wing PKCS#8 (standard "PRIVATE KEY" PEM with id-XWing OID) + if block.Type == "PRIVATE KEY" { + if seed, err := parseXWingPrivateKeyFromDER(block.Bytes); err == nil { + return &XWingDecryptor{seed: seed}, nil + } + } + + if block.Type == "MLKEM DECAPSULATION KEY" { + decap768, err := mlkem.NewDecapsulationKey768(block.Bytes) + if err == nil { + return &MLKEMDecryptor768{decap: decap768}, nil + } + decap1024, err1024 := mlkem.NewDecapsulationKey1024(block.Bytes) + if err1024 != nil { + return nil, fmt.Errorf("mlkem.NewDecapsulationKey1024 failed after mlkem.NewDecapsulationKey768 failed: %w / %w", err, err1024) + } + return &MLKEMDecryptor1024{decap: decap1024}, nil + } + priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) switch { case err == nil: @@ -109,6 +144,13 @@ func (asymDecryption AsymDecryption) Decrypt(data []byte) ([]byte, error) { return bytes, nil } +func (asymDecryption AsymDecryption) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if len(ephemeral) > 0 { + return nil, errors.New("ephemeral key is not supported for RSA decryption") + } + return asymDecryption.Decrypt(data) +} + type ECDecryptor struct { sk *ecdh.PrivateKey salt []byte @@ -196,6 +238,88 @@ func (e ECDecryptor) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, er return plaintext, nil } +func (d MLKEMDecryptor768) Decrypt(_ []byte) ([]byte, error) { + return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") +} + +func (d MLKEMDecryptor1024) Decrypt(_ []byte) ([]byte, error) { + return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") +} + +func (d MLKEMDecryptor768) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if d.decap == nil { + return nil, errors.New("mlkem decapsulation key is nil") + } + if len(ephemeral) == 0 { + return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") + } + + sharedSecret, err := d.decap.Decapsulate(ephemeral) + if err != nil { + return nil, fmt.Errorf("mlkem.Decapsulate failed: %w", err) + } + + block, err := aes.NewCipher(sharedSecret) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failure: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failure: %w", err) + } + + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("gcm.Open failure: %w", err) + } + + return plaintext, nil +} + +func (d MLKEMDecryptor1024) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + if d.decap == nil { + return nil, errors.New("mlkem decapsulation key is nil") + } + if len(ephemeral) == 0 { + return nil, errors.New("ciphertext encapsulation is required for ML-KEM decryption") + } + + sharedSecret, err := d.decap.Decapsulate(ephemeral) + if err != nil { + return nil, fmt.Errorf("mlkem.Decapsulate failed: %w", err) + } + + block, err := aes.NewCipher(sharedSecret) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failure: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failure: %w", err) + } + + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("gcm.Open failure: %w", err) + } + + return plaintext, nil +} + func convCurve(c ecdh.Curve) elliptic.Curve { switch c { case ecdh.P256(): @@ -208,3 +332,47 @@ func convCurve(c ecdh.Curve) elliptic.Curve { return nil } } + +// --- XWingDecryptor --- + +func (d XWingDecryptor) Decrypt(_ []byte) ([]byte, error) { + return nil, errors.New("X-Wing requires ephemeral ciphertext for decryption") +} + +func (d XWingDecryptor) DecryptWithEphemeralKey(data, ephemeral []byte) ([]byte, error) { + // Parse ASN.1-wrapped ciphertext + ct, err := parseXWingCiphertext(ephemeral) + if err != nil { + return nil, fmt.Errorf("xwing: failed to parse ciphertext: %w", err) + } + + // Perform X-Wing decapsulation + sharedSecret, err := xwingDecapsulate(ct, d.seed) + if err != nil { + return nil, fmt.Errorf("xwing: decapsulation failed: %w", err) + } + + // Decrypt AES-GCM wrapped data + block, err := aes.NewCipher(sharedSecret[:]) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failure: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failure: %w", err) + } + + nonceSize := gcm.NonceSize() + if len(data) < nonceSize { + return nil, errors.New("ciphertext too short") + } + + nonce, ciphertext := data[:nonceSize], data[nonceSize:] + plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) + if err != nil { + return nil, fmt.Errorf("gcm.Open failure: %w", err) + } + + return plaintext, nil +} diff --git a/lib/ocrypto/asym_encryption.go b/lib/ocrypto/asym_encryption.go index c44aa64cce..bfba478cbb 100644 --- a/lib/ocrypto/asym_encryption.go +++ b/lib/ocrypto/asym_encryption.go @@ -5,6 +5,7 @@ import ( "crypto/cipher" "crypto/ecdh" "crypto/ecdsa" + "crypto/mlkem" "crypto/rand" "crypto/rsa" "crypto/sha1" //nolint:gosec // used for padding which is safe @@ -23,8 +24,10 @@ import ( type SchemeType string const ( - RSA SchemeType = "wrapped" - EC SchemeType = "ec-wrapped" + RSA SchemeType = "wrapped" + EC SchemeType = "ec-wrapped" + MLKEM SchemeType = "mlkem-wrapped" + Hybrid SchemeType = "hybrid" ) type PublicKeyEncryptor interface { @@ -59,6 +62,24 @@ type ECEncryptor struct { info []byte } +type MLKEMEncryptor768 struct { + pub *mlkem.EncapsulationKey768 + cipherText []byte + sharedSecret []byte +} + +type MLKEMEncryptor1024 struct { + pub *mlkem.EncapsulationKey1024 + cipherText []byte + sharedSecret []byte +} + +type XWingEncryptor struct { + pk []byte // 1216-byte X-Wing encapsulation key + cipherText []byte // 1120-byte X-Wing ciphertext (ct_M || ct_X) + sharedSecret []byte // 32-byte combined shared secret +} + func FromPublicPEM(publicKeyInPem string) (PublicKeyEncryptor, error) { // TK Move salt and info out of library, into API option functions digest := sha256.New() @@ -85,6 +106,12 @@ func FromPublicPEMWithSalt(publicKeyInPem string, salt, info []byte) (PublicKeyE return newECIES(e, salt, info) case *ecdh.PublicKey: return newECIES(pub, salt, info) + case *mlkem.EncapsulationKey768: + return newMLKEM768(pub), nil + case *mlkem.EncapsulationKey1024: + return newMLKEM1024(pub), nil + case xwingPublicKey: + return newXWingEncryptor([]byte(pub)) default: break } @@ -97,6 +124,16 @@ func newECIES(pub *ecdh.PublicKey, salt, info []byte) (ECEncryptor, error) { return ECEncryptor{pub, ek, salt, info}, err } +func newMLKEM768(pub *mlkem.EncapsulationKey768) *MLKEMEncryptor768 { + sharedSecret, cipherText := pub.Encapsulate() + return &MLKEMEncryptor768{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} +} + +func newMLKEM1024(pub *mlkem.EncapsulationKey1024) *MLKEMEncryptor1024 { + sharedSecret, cipherText := pub.Encapsulate() + return &MLKEMEncryptor1024{pub: pub, cipherText: cipherText, sharedSecret: sharedSecret} +} + // NewAsymEncryption creates and returns a new AsymEncryption. // // Deprecated: Use FromPublicPEM instead. @@ -116,6 +153,10 @@ func NewAsymEncryption(publicKeyInPem string) (AsymEncryption, error) { return AsymEncryption{}, fmt.Errorf("unsupported public key type: %T", pub) } +// xwingPublicKey is a wrapper type for the raw 1216-byte X-Wing public key +// used for type-switching in FromPublicPEMWithSalt. +type xwingPublicKey []byte + func getPublicPart(publicKeyInPem string) (any, error) { block, _ := pem.Decode([]byte(publicKeyInPem)) if block == nil { @@ -123,14 +164,31 @@ func getPublicPart(publicKeyInPem string) (any, error) { } var pub any - if strings.Contains(publicKeyInPem, "BEGIN CERTIFICATE") { + switch { + case strings.Contains(publicKeyInPem, "BEGIN CERTIFICATE"): cert, err := x509.ParseCertificate(block.Bytes) if err != nil { return nil, fmt.Errorf("x509.ParseCertificate failed: %w", err) } pub = cert.PublicKey - } else { + case block.Type == "MLKEM ENCAPSULATOR": + encap768, err := mlkem.NewEncapsulationKey768(block.Bytes) + if err == nil { + pub = encap768 + break + } + encap1024, err1024 := mlkem.NewEncapsulationKey1024(block.Bytes) + if err1024 != nil { + return nil, fmt.Errorf("mlkem.NewEncapsulationKey1024 failed after mlkem.NewEncapsulationKey768 failed: %w / %w", err, err1024) + } + pub = encap1024 + default: + // Try X-Wing SubjectPublicKeyInfo first (has id-XWing OID) + if pk, err := parseXWingPublicKeyFromDER(block.Bytes); err == nil { + pub = xwingPublicKey(pk) + break + } var err error pub, err = x509.ParsePKIXPublicKey(block.Bytes) if err != nil { @@ -160,6 +218,14 @@ func (e ECEncryptor) Type() SchemeType { return EC } +func (e MLKEMEncryptor768) Type() SchemeType { + return MLKEM +} + +func (e MLKEMEncryptor1024) Type() SchemeType { + return MLKEM +} + func (e ECEncryptor) KeyType() KeyType { switch e.pub.Curve() { case ecdh.P256(): @@ -176,6 +242,14 @@ func (e ECEncryptor) KeyType() KeyType { } } +func (e MLKEMEncryptor768) KeyType() KeyType { + return MLKEM768Key +} + +func (e MLKEMEncryptor1024) KeyType() KeyType { + return MLKEM1024Key +} + func (e AsymEncryption) EphemeralKey() []byte { return nil } @@ -188,6 +262,14 @@ func (e ECEncryptor) EphemeralKey() []byte { return publicKeyBytes } +func (e MLKEMEncryptor768) EphemeralKey() []byte { + return e.cipherText +} + +func (e MLKEMEncryptor1024) EphemeralKey() []byte { + return e.cipherText +} + func (e AsymEncryption) Metadata() (map[string]string, error) { return make(map[string]string), nil } @@ -198,6 +280,18 @@ func (e ECEncryptor) Metadata() (map[string]string, error) { return m, nil } +func (e MLKEMEncryptor768) Metadata() (map[string]string, error) { + m := make(map[string]string) + m["encapsulatedKey"] = string(e.EphemeralKey()) + return m, nil +} + +func (e MLKEMEncryptor1024) Metadata() (map[string]string, error) { + m := make(map[string]string) + m["encapsulatedKey"] = string(e.EphemeralKey()) + return m, nil +} + func (e AsymEncryption) Encrypt(data []byte) ([]byte, error) { if e.PublicKey == nil { return nil, errors.New("failed to encrypt, public key is empty") @@ -269,7 +363,125 @@ func (e ECEncryptor) Encrypt(data []byte) ([]byte, error) { return ciphertext, nil } +func (e MLKEMEncryptor768) Encrypt(data []byte) ([]byte, error) { + block, err := aes.NewCipher(e.sharedSecret) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failed: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failed: %w", err) + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, fmt.Errorf("nonce generation failed: %w", err) + } + + return gcm.Seal(nonce, nonce, data, nil), nil +} + +func (e MLKEMEncryptor1024) Encrypt(data []byte) ([]byte, error) { + block, err := aes.NewCipher(e.sharedSecret) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failed: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failed: %w", err) + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, fmt.Errorf("nonce generation failed: %w", err) + } + + return gcm.Seal(nonce, nonce, data, nil), nil +} + // PublicKeyInPemFormat Returns public key in pem format. func (e ECEncryptor) PublicKeyInPemFormat() (string, error) { return publicKeyInPemFormat(e.ek.Public()) } + +func (e MLKEMEncryptor768) PublicKeyInPemFormat() (string, error) { + return string(pem.EncodeToMemory(&pem.Block{ + Type: "MLKEM ENCAPSULATOR", + Bytes: e.pub.Bytes(), + })), nil +} + +func (e MLKEMEncryptor1024) PublicKeyInPemFormat() (string, error) { + return string(pem.EncodeToMemory(&pem.Block{ + Type: "MLKEM ENCAPSULATOR", + Bytes: e.pub.Bytes(), + })), nil +} + +// --- XWingEncryptor --- + +func newXWingEncryptor(pk []byte) (*XWingEncryptor, error) { + ss, ct, err := xwingEncapsulate(pk) + if err != nil { + return nil, err + } + return &XWingEncryptor{ + pk: pk, + cipherText: ct[:], + sharedSecret: ss[:], + }, nil +} + +func (e XWingEncryptor) Type() SchemeType { + return Hybrid +} + +func (e XWingEncryptor) KeyType() KeyType { + return HybridXWing +} + +func (e XWingEncryptor) EphemeralKey() []byte { + ct, err := marshalXWingCiphertext(e.cipherText) + if err != nil { + return nil + } + return ct +} + +func (e XWingEncryptor) Metadata() (map[string]string, error) { + m := make(map[string]string) + m["encapsulatedKey"] = string(e.EphemeralKey()) + return m, nil +} + +func (e XWingEncryptor) Encrypt(data []byte) ([]byte, error) { + block, err := aes.NewCipher(e.sharedSecret) + if err != nil { + return nil, fmt.Errorf("aes.NewCipher failed: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, fmt.Errorf("cipher.NewGCM failed: %w", err) + } + + nonce := make([]byte, gcm.NonceSize()) + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, fmt.Errorf("nonce generation failed: %w", err) + } + + return gcm.Seal(nonce, nonce, data, nil), nil +} + +func (e XWingEncryptor) PublicKeyInPemFormat() (string, error) { + der, err := marshalXWingPublicKey(e.pk) + if err != nil { + return "", err + } + return string(pem.EncodeToMemory(&pem.Block{ + Type: "PUBLIC KEY", + Bytes: der, + })), nil +} diff --git a/lib/ocrypto/ec_key_pair.go b/lib/ocrypto/ec_key_pair.go index f9a9554d4f..843236b36b 100644 --- a/lib/ocrypto/ec_key_pair.go +++ b/lib/ocrypto/ec_key_pair.go @@ -4,6 +4,7 @@ import ( "crypto/ecdh" "crypto/ecdsa" "crypto/elliptic" + "crypto/mlkem" "crypto/rand" "crypto/sha256" "crypto/x509" @@ -22,11 +23,14 @@ type ECCMode uint8 type KeyType string const ( - RSA2048Key KeyType = "rsa:2048" - RSA4096Key KeyType = "rsa:4096" - EC256Key KeyType = "ec:secp256r1" - EC384Key KeyType = "ec:secp384r1" - EC521Key KeyType = "ec:secp521r1" + RSA2048Key KeyType = "rsa:2048" + RSA4096Key KeyType = "rsa:4096" + EC256Key KeyType = "ec:secp256r1" + EC384Key KeyType = "ec:secp384r1" + EC521Key KeyType = "ec:secp521r1" + MLKEM768Key KeyType = "mlkem:768" + MLKEM1024Key KeyType = "mlkem:1024" + HybridXWing KeyType = "hpqt:xwing" ) const ( @@ -64,6 +68,12 @@ func NewKeyPair(kt KeyType) (KeyPair, error) { return nil, err } return NewECKeyPair(mode) + case MLKEM768Key: + return NewMLKEMKeyPair() + case MLKEM1024Key: + return NewMLKEM1024KeyPair() + case HybridXWing: + return NewXWingKeyPair() default: return nil, fmt.Errorf("unsupported key type: %v", kt) } @@ -73,6 +83,14 @@ type ECKeyPair struct { PrivateKey *ecdsa.PrivateKey } +type MLKEMKeyPair struct { + PrivateKey *mlkem.DecapsulationKey768 +} + +type MLKEM1024KeyPair struct { + PrivateKey *mlkem.DecapsulationKey1024 +} + func IsECKeyType(kt KeyType) bool { switch kt { //nolint:exhaustive // only handle ec types case EC256Key, EC384Key, EC521Key: @@ -184,6 +202,24 @@ func NewECKeyPair(mode ECCMode) (ECKeyPair, error) { return ecKeyPair, nil } +func NewMLKEMKeyPair() (MLKEMKeyPair, error) { + privateKey, err := mlkem.GenerateKey768() + if err != nil { + return MLKEMKeyPair{}, fmt.Errorf("mlkem.GenerateKey768 failed: %w", err) + } + + return MLKEMKeyPair{PrivateKey: privateKey}, nil +} + +func NewMLKEM1024KeyPair() (MLKEM1024KeyPair, error) { + privateKey, err := mlkem.GenerateKey1024() + if err != nil { + return MLKEM1024KeyPair{}, fmt.Errorf("mlkem.GenerateKey1024 failed: %w", err) + } + + return MLKEM1024KeyPair{PrivateKey: privateKey}, nil +} + // PrivateKeyInPemFormat Returns private key in pem format. func (keyPair ECKeyPair) PrivateKeyInPemFormat() (string, error) { if keyPair.PrivateKey == nil { @@ -224,6 +260,62 @@ func (keyPair ECKeyPair) PublicKeyInPemFormat() (string, error) { return string(publicKeyPem), nil } +func (keyPair MLKEMKeyPair) PrivateKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted private key") + } + + privateKeyPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "MLKEM DECAPSULATION KEY", + Bytes: keyPair.PrivateKey.Bytes(), + }, + ) + return string(privateKeyPEM), nil +} + +func (keyPair MLKEMKeyPair) PublicKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted public key") + } + + publicKeyPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "MLKEM ENCAPSULATOR", + Bytes: keyPair.PrivateKey.EncapsulationKey().Bytes(), + }, + ) + return string(publicKeyPEM), nil +} + +func (keyPair MLKEM1024KeyPair) PrivateKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted private key") + } + + privateKeyPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "MLKEM DECAPSULATION KEY", + Bytes: keyPair.PrivateKey.Bytes(), + }, + ) + return string(privateKeyPEM), nil +} + +func (keyPair MLKEM1024KeyPair) PublicKeyInPemFormat() (string, error) { + if keyPair.PrivateKey == nil { + return "", errors.New("failed to generate PEM formatted public key") + } + + publicKeyPEM := pem.EncodeToMemory( + &pem.Block{ + Type: "MLKEM ENCAPSULATOR", + Bytes: keyPair.PrivateKey.EncapsulationKey().Bytes(), + }, + ) + return string(publicKeyPEM), nil +} + // KeySize Return the size of this ec key pair. func (keyPair ECKeyPair) KeySize() (int, error) { if keyPair.PrivateKey == nil { @@ -494,3 +586,11 @@ func GetECKeySize(pemData []byte) (int, error) { func (keyPair ECKeyPair) GetKeyType() KeyType { return EC256Key } + +func (keyPair MLKEMKeyPair) GetKeyType() KeyType { + return MLKEM768Key +} + +func (keyPair MLKEM1024KeyPair) GetKeyType() KeyType { + return MLKEM1024Key +} diff --git a/lib/ocrypto/xwing.go b/lib/ocrypto/xwing.go new file mode 100644 index 0000000000..78de9e682c --- /dev/null +++ b/lib/ocrypto/xwing.go @@ -0,0 +1,329 @@ +package ocrypto + +import ( + "crypto/ecdh" + "crypto/mlkem" + "crypto/rand" + "crypto/sha3" + "encoding/asn1" + "encoding/pem" + "errors" + "fmt" +) + +// X-Wing OID: 1.3.6.1.4.1.62253.25722 +// Per draft-connolly-cfrg-xwing-kem-10 +var oidXWing = asn1.ObjectIdentifier{1, 3, 6, 1, 4, 1, 62253, 25722} + +// xwingLabel is the 6-byte ASCII label used in the X-Wing combiner. +// "\./"+"/^\" = 0x5c2e2f2f5e5c +var xwingLabel = []byte{0x5c, 0x2e, 0x2f, 0x2f, 0x5e, 0x5c} + +const ( + xwingSeedSize = 32 + xwingExpandedSize = 96 + xwingPublicKeySize = 1216 // 1184 (ML-KEM-768 encap key) + 32 (X25519 pub) + xwingCiphertextSize = 1120 // 1088 (ML-KEM-768 ct) + 32 (X25519 ct) + xwingSharedKeySize = 32 + mlkem768EncapSize = 1184 + mlkem768CTSize = 1088 + x25519KeySize = 32 +) + +// XWingKeyPair holds the 32-byte seed that is the X-Wing decapsulation key. +type XWingKeyPair struct { + seed [xwingSeedSize]byte +} + +// NewXWingKeyPair generates a new X-Wing key pair from a random seed. +func NewXWingKeyPair() (XWingKeyPair, error) { + var seed [xwingSeedSize]byte + if _, err := rand.Read(seed[:]); err != nil { + return XWingKeyPair{}, fmt.Errorf("xwing: failed to generate random seed: %w", err) + } + return XWingKeyPair{seed: seed}, nil +} + +// expandDecapsulationKey derives the component keys from the 32-byte seed. +// Returns (mlkemDecapKey, x25519PrivateKey, mlkemEncapKey, x25519PublicKey). +func expandDecapsulationKey(seed [xwingSeedSize]byte) (*mlkem.DecapsulationKey768, *ecdh.PrivateKey, []byte, []byte, error) { + // SHAKE256(seed, 96*8) → 96 bytes + expanded := sha3.SumSHAKE256(seed[:], xwingExpandedSize) + + // d = expanded[0:32], z = expanded[32:64] → ML-KEM-768 seed + mlkemSeed := expanded[0:64] // d || z + skM, err := mlkem.NewDecapsulationKey768(mlkemSeed) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("xwing: mlkem.NewDecapsulationKey768 failed: %w", err) + } + pkM := skM.EncapsulationKey().Bytes() + + // sk_X = expanded[64:96] + skX, err := ecdh.X25519().NewPrivateKey(expanded[64:96]) + if err != nil { + return nil, nil, nil, nil, fmt.Errorf("xwing: X25519 NewPrivateKey failed: %w", err) + } + pkX := skX.PublicKey().Bytes() + + return skM, skX, pkM, pkX, nil +} + +// xwingCombiner computes SHA3-256(ss_M || ss_X || ct_X || pk_X || XWingLabel). +func xwingCombiner(ssM, ssX, ctX, pkX []byte) [xwingSharedKeySize]byte { + var combined []byte + combined = append(combined, ssM...) + combined = append(combined, ssX...) + combined = append(combined, ctX...) + combined = append(combined, pkX...) + combined = append(combined, xwingLabel...) + return sha3.Sum256(combined) +} + +// xwingEncapsulate performs X-Wing encapsulation against a 1216-byte public key. +// Returns the 32-byte shared secret and 1120-byte ciphertext. +func xwingEncapsulate(pk []byte) ([xwingSharedKeySize]byte, [xwingCiphertextSize]byte, error) { + var ss [xwingSharedKeySize]byte + var ct [xwingCiphertextSize]byte + + if len(pk) != xwingPublicKeySize { + return ss, ct, fmt.Errorf("xwing: invalid public key size %d, expected %d", len(pk), xwingPublicKeySize) + } + + pkM := pk[:mlkem768EncapSize] + pkX := pk[mlkem768EncapSize:] + + // ML-KEM-768 encapsulation + encapKey, err := mlkem.NewEncapsulationKey768(pkM) + if err != nil { + return ss, ct, fmt.Errorf("xwing: mlkem encapsulation key parse failed: %w", err) + } + ssM, ctM := encapKey.Encapsulate() + + // X25519 ephemeral key exchange + ekX, err := ecdh.X25519().GenerateKey(rand.Reader) + if err != nil { + return ss, ct, fmt.Errorf("xwing: X25519 GenerateKey failed: %w", err) + } + ctX := ekX.PublicKey().Bytes() + + pkXKey, err := ecdh.X25519().NewPublicKey(pkX) + if err != nil { + return ss, ct, fmt.Errorf("xwing: X25519 NewPublicKey failed: %w", err) + } + ssX, err := ekX.ECDH(pkXKey) + if err != nil { + return ss, ct, fmt.Errorf("xwing: X25519 ECDH failed: %w", err) + } + + // Combiner + ss = xwingCombiner(ssM, ssX, ctX, pkX) + + // Ciphertext = ct_M || ct_X + copy(ct[:mlkem768CTSize], ctM) + copy(ct[mlkem768CTSize:], ctX) + + return ss, ct, nil +} + +// xwingDecapsulate performs X-Wing decapsulation using the 32-byte seed. +func xwingDecapsulate(ct []byte, seed [xwingSeedSize]byte) ([xwingSharedKeySize]byte, error) { + var ss [xwingSharedKeySize]byte + + if len(ct) != xwingCiphertextSize { + return ss, fmt.Errorf("xwing: invalid ciphertext size %d, expected %d", len(ct), xwingCiphertextSize) + } + + skM, skX, _, pkX, err := expandDecapsulationKey(seed) + if err != nil { + return ss, err + } + + ctM := ct[:mlkem768CTSize] + ctX := ct[mlkem768CTSize:] + + // ML-KEM-768 decapsulation + ssM, err := skM.Decapsulate(ctM) + if err != nil { + return ss, fmt.Errorf("xwing: mlkem decapsulate failed: %w", err) + } + + // X25519 key exchange with ephemeral ciphertext + ctXKey, err := ecdh.X25519().NewPublicKey(ctX) + if err != nil { + return ss, fmt.Errorf("xwing: X25519 NewPublicKey(ctX) failed: %w", err) + } + ssX, err := skX.ECDH(ctXKey) + if err != nil { + return ss, fmt.Errorf("xwing: X25519 ECDH failed: %w", err) + } + + ss = xwingCombiner(ssM, ssX, ctX, pkX) + return ss, nil +} + +// --- ASN.1 structures for X-Wing key and ciphertext encoding --- + +// pkixAlgorithmIdentifier represents an ASN.1 AlgorithmIdentifier. +type pkixAlgorithmIdentifier struct { + Algorithm asn1.ObjectIdentifier +} + +// subjectPublicKeyInfo represents ASN.1 SubjectPublicKeyInfo. +type subjectPublicKeyInfo struct { + Algorithm pkixAlgorithmIdentifier + PublicKey asn1.BitString +} + +// pkcs8PrivateKey represents ASN.1 OneAsymmetricKey / PKCS#8. +type pkcs8PrivateKey struct { + Version int + Algorithm pkixAlgorithmIdentifier + PrivateKey []byte +} + +// xwingCiphertextASN1 wraps the X-Wing ciphertext with its OID for self-description. +type xwingCiphertextASN1 struct { + Algorithm asn1.ObjectIdentifier + Ciphertext []byte +} + +// marshalXWingPublicKey encodes a 1216-byte X-Wing public key as DER SubjectPublicKeyInfo. +func marshalXWingPublicKey(pk []byte) ([]byte, error) { + spki := subjectPublicKeyInfo{ + Algorithm: pkixAlgorithmIdentifier{Algorithm: oidXWing}, + PublicKey: asn1.BitString{Bytes: pk, BitLength: len(pk) * 8}, //nolint:mnd // bits per byte + } + return asn1.Marshal(spki) +} + +// marshalXWingPrivateKey encodes a 32-byte X-Wing seed as DER PKCS#8. +func marshalXWingPrivateKey(seed []byte) ([]byte, error) { + // The privateKey field is an OCTET STRING containing the seed, + // which itself is DER-encoded as an OCTET STRING. + innerOctet, err := asn1.Marshal(seed) + if err != nil { + return nil, fmt.Errorf("xwing: failed to marshal seed: %w", err) + } + pk8 := pkcs8PrivateKey{ + Version: 0, + Algorithm: pkixAlgorithmIdentifier{Algorithm: oidXWing}, + PrivateKey: innerOctet, + } + return asn1.Marshal(pk8) +} + +// marshalXWingCiphertext wraps ciphertext bytes with OID for self-describing encoding. +func marshalXWingCiphertext(ct []byte) ([]byte, error) { + return asn1.Marshal(xwingCiphertextASN1{ + Algorithm: oidXWing, + Ciphertext: ct, + }) +} + +// parseXWingCiphertext extracts raw ciphertext from ASN.1 wrapped form. +func parseXWingCiphertext(data []byte) ([]byte, error) { + var ct xwingCiphertextASN1 + rest, err := asn1.Unmarshal(data, &ct) + if err != nil { + return nil, fmt.Errorf("xwing: failed to unmarshal ciphertext ASN.1: %w", err) + } + if len(rest) > 0 { + return nil, errors.New("xwing: trailing data after ciphertext ASN.1") + } + if !ct.Algorithm.Equal(oidXWing) { + return nil, fmt.Errorf("xwing: unexpected OID in ciphertext: %v", ct.Algorithm) + } + if len(ct.Ciphertext) != xwingCiphertextSize { + return nil, fmt.Errorf("xwing: invalid ciphertext size %d in ASN.1", len(ct.Ciphertext)) + } + return ct.Ciphertext, nil +} + +// --- KeyPair interface implementation --- + +func (kp XWingKeyPair) GetKeyType() KeyType { + return HybridXWing +} + +func (kp XWingKeyPair) PublicKeyInPemFormat() (string, error) { + _, _, pkM, pkX, err := expandDecapsulationKey(kp.seed) + if err != nil { + return "", err + } + + pk := make([]byte, 0, xwingPublicKeySize) + pk = append(pk, pkM...) + pk = append(pk, pkX...) + + der, err := marshalXWingPublicKey(pk) + if err != nil { + return "", err + } + + return string(pem.EncodeToMemory(&pem.Block{ + Type: "PUBLIC KEY", + Bytes: der, + })), nil +} + +func (kp XWingKeyPair) PrivateKeyInPemFormat() (string, error) { + der, err := marshalXWingPrivateKey(kp.seed[:]) + if err != nil { + return "", err + } + + return string(pem.EncodeToMemory(&pem.Block{ + Type: "PRIVATE KEY", + Bytes: der, + })), nil +} + +// parseXWingPublicKeyFromDER parses a DER-encoded SubjectPublicKeyInfo and returns +// the raw 1216-byte X-Wing public key if the OID matches. +func parseXWingPublicKeyFromDER(der []byte) ([]byte, error) { + var spki subjectPublicKeyInfo + rest, err := asn1.Unmarshal(der, &spki) + if err != nil { + return nil, err + } + if len(rest) > 0 { + return nil, errors.New("xwing: trailing data after SubjectPublicKeyInfo") + } + if !spki.Algorithm.Algorithm.Equal(oidXWing) { + return nil, fmt.Errorf("xwing: unexpected OID: %v", spki.Algorithm.Algorithm) + } + pk := spki.PublicKey.Bytes + if len(pk) != xwingPublicKeySize { + return nil, fmt.Errorf("xwing: invalid public key size %d", len(pk)) + } + return pk, nil +} + +// parseXWingPrivateKeyFromDER parses a DER-encoded PKCS#8 and returns +// the 32-byte X-Wing seed if the OID matches. +func parseXWingPrivateKeyFromDER(der []byte) ([xwingSeedSize]byte, error) { + var seed [xwingSeedSize]byte + var pk8 pkcs8PrivateKey + rest, err := asn1.Unmarshal(der, &pk8) + if err != nil { + return seed, fmt.Errorf("xwing: failed to unmarshal PKCS#8: %w", err) + } + if len(rest) > 0 { + return seed, errors.New("xwing: trailing data after PKCS#8") + } + if !pk8.Algorithm.Algorithm.Equal(oidXWing) { + return seed, fmt.Errorf("xwing: unexpected OID: %v", pk8.Algorithm.Algorithm) + } + + // The privateKey is a DER-encoded OCTET STRING containing the seed. + var seedBytes []byte + _, err = asn1.Unmarshal(pk8.PrivateKey, &seedBytes) + if err != nil { + return seed, fmt.Errorf("xwing: failed to unmarshal seed from PKCS#8: %w", err) + } + if len(seedBytes) != xwingSeedSize { + return seed, fmt.Errorf("xwing: invalid seed size %d", len(seedBytes)) + } + copy(seed[:], seedBytes) + return seed, nil +} diff --git a/lib/ocrypto/xwing_test.go b/lib/ocrypto/xwing_test.go new file mode 100644 index 0000000000..7e05f85f7d --- /dev/null +++ b/lib/ocrypto/xwing_test.go @@ -0,0 +1,387 @@ +package ocrypto + +import ( + "bytes" + "encoding/hex" + "testing" +) + +func TestXWingKeyPairGeneration(t *testing.T) { + kp, err := NewXWingKeyPair() + if err != nil { + t.Fatalf("NewXWingKeyPair failed: %v", err) + } + + pubPEM, err := kp.PublicKeyInPemFormat() + if err != nil { + t.Fatalf("PublicKeyInPemFormat failed: %v", err) + } + if pubPEM == "" { + t.Fatal("empty public key PEM") + } + + privPEM, err := kp.PrivateKeyInPemFormat() + if err != nil { + t.Fatalf("PrivateKeyInPemFormat failed: %v", err) + } + if privPEM == "" { + t.Fatal("empty private key PEM") + } + + if kp.GetKeyType() != HybridXWing { + t.Fatalf("expected key type %s, got %s", HybridXWing, kp.GetKeyType()) + } +} + +func TestXWingKeyPairViaFactory(t *testing.T) { + kp, err := NewKeyPair(HybridXWing) + if err != nil { + t.Fatalf("NewKeyPair(HybridXWing) failed: %v", err) + } + if kp.GetKeyType() != HybridXWing { + t.Fatalf("expected key type %s, got %s", HybridXWing, kp.GetKeyType()) + } +} + +func TestXWingPEMRoundTrip(t *testing.T) { + kp, err := NewXWingKeyPair() + if err != nil { + t.Fatalf("NewXWingKeyPair failed: %v", err) + } + + // Public key round-trip + pubPEM, err := kp.PublicKeyInPemFormat() + if err != nil { + t.Fatalf("PublicKeyInPemFormat failed: %v", err) + } + + enc, err := FromPublicPEM(pubPEM) + if err != nil { + t.Fatalf("FromPublicPEM failed for X-Wing public key: %v", err) + } + if enc.Type() != Hybrid { + t.Fatalf("expected scheme type %s, got %s", Hybrid, enc.Type()) + } + if enc.KeyType() != HybridXWing { + t.Fatalf("expected key type %s, got %s", HybridXWing, enc.KeyType()) + } + + // Private key round-trip + privPEM, err := kp.PrivateKeyInPemFormat() + if err != nil { + t.Fatalf("PrivateKeyInPemFormat failed: %v", err) + } + + dec, err := FromPrivatePEM(privPEM) + if err != nil { + t.Fatalf("FromPrivatePEM failed for X-Wing private key: %v", err) + } + if _, ok := dec.(*XWingDecryptor); !ok { + t.Fatalf("expected XWingDecryptor, got %T", dec) + } +} + +func TestXWingEncryptDecryptRoundTrip(t *testing.T) { + kp, err := NewXWingKeyPair() + if err != nil { + t.Fatalf("NewXWingKeyPair failed: %v", err) + } + + pubPEM, err := kp.PublicKeyInPemFormat() + if err != nil { + t.Fatalf("PublicKeyInPemFormat failed: %v", err) + } + + privPEM, err := kp.PrivateKeyInPemFormat() + if err != nil { + t.Fatalf("PrivateKeyInPemFormat failed: %v", err) + } + + // Encrypt + enc, err := FromPublicPEM(pubPEM) + if err != nil { + t.Fatalf("FromPublicPEM failed: %v", err) + } + + plaintext := []byte("hello, X-Wing hybrid KEM!") + ciphertext, err := enc.Encrypt(plaintext) + if err != nil { + t.Fatalf("Encrypt failed: %v", err) + } + + ephemeralKey := enc.EphemeralKey() + if len(ephemeralKey) == 0 { + t.Fatal("EphemeralKey returned empty") + } + + // Decrypt + dec, err := FromPrivatePEM(privPEM) + if err != nil { + t.Fatalf("FromPrivatePEM failed: %v", err) + } + + recovered, err := dec.DecryptWithEphemeralKey(ciphertext, ephemeralKey) + if err != nil { + t.Fatalf("DecryptWithEphemeralKey failed: %v", err) + } + + if !bytes.Equal(plaintext, recovered) { + t.Fatalf("plaintext mismatch: got %q, want %q", recovered, plaintext) + } +} + +func TestXWingMultipleEncryptions(t *testing.T) { + kp, err := NewXWingKeyPair() + if err != nil { + t.Fatalf("NewXWingKeyPair failed: %v", err) + } + + pubPEM, err := kp.PublicKeyInPemFormat() + if err != nil { + t.Fatalf("PublicKeyInPemFormat failed: %v", err) + } + + privPEM, err := kp.PrivateKeyInPemFormat() + if err != nil { + t.Fatalf("PrivateKeyInPemFormat failed: %v", err) + } + + for i := range 5 { + plaintext := []byte("message " + string(rune('A'+i))) + + enc, err := FromPublicPEM(pubPEM) + if err != nil { + t.Fatalf("FromPublicPEM failed: %v", err) + } + + ct, err := enc.Encrypt(plaintext) + if err != nil { + t.Fatalf("Encrypt failed: %v", err) + } + + ek := enc.EphemeralKey() + + dec, err := FromPrivatePEM(privPEM) + if err != nil { + t.Fatalf("FromPrivatePEM failed: %v", err) + } + + recovered, err := dec.DecryptWithEphemeralKey(ct, ek) + if err != nil { + t.Fatalf("DecryptWithEphemeralKey failed for message %d: %v", i, err) + } + + if !bytes.Equal(plaintext, recovered) { + t.Fatalf("plaintext mismatch for message %d", i) + } + } +} + +func TestXWingCombiner(t *testing.T) { + // Verify the combiner produces deterministic output + ssM := make([]byte, 32) + ssX := make([]byte, 32) + ctX := make([]byte, 32) + pkX := make([]byte, 32) + + result1 := xwingCombiner(ssM, ssX, ctX, pkX) + result2 := xwingCombiner(ssM, ssX, ctX, pkX) + + if result1 != result2 { + t.Fatal("combiner not deterministic") + } + + // Different inputs should produce different outputs + ssM[0] = 1 + result3 := xwingCombiner(ssM, ssX, ctX, pkX) + if result1 == result3 { + t.Fatal("combiner should produce different output for different inputs") + } +} + +func TestXWingLabel(t *testing.T) { + // Verify the label matches the spec: 0x5c2e2f2f5e5c + expected, _ := hex.DecodeString("5c2e2f2f5e5c") + if !bytes.Equal(xwingLabel, expected) { + t.Fatalf("XWing label mismatch: got %x, want %x", xwingLabel, expected) + } +} + +func TestXWingCiphertextASN1RoundTrip(t *testing.T) { + // Create a fake 1120-byte ciphertext + ct := make([]byte, xwingCiphertextSize) + for i := range ct { + ct[i] = byte(i % 256) + } + + encoded, err := marshalXWingCiphertext(ct) + if err != nil { + t.Fatalf("marshalXWingCiphertext failed: %v", err) + } + + decoded, err := parseXWingCiphertext(encoded) + if err != nil { + t.Fatalf("parseXWingCiphertext failed: %v", err) + } + + if !bytes.Equal(ct, decoded) { + t.Fatal("ciphertext round-trip mismatch") + } +} + +func TestXWingPublicKeyASN1RoundTrip(t *testing.T) { + // Create a fake 1216-byte public key + pk := make([]byte, xwingPublicKeySize) + for i := range pk { + pk[i] = byte(i % 256) + } + + der, err := marshalXWingPublicKey(pk) + if err != nil { + t.Fatalf("marshalXWingPublicKey failed: %v", err) + } + + decoded, err := parseXWingPublicKeyFromDER(der) + if err != nil { + t.Fatalf("parseXWingPublicKeyFromDER failed: %v", err) + } + + if !bytes.Equal(pk, decoded) { + t.Fatal("public key round-trip mismatch") + } +} + +func TestXWingPrivateKeyASN1RoundTrip(t *testing.T) { + seed := [32]byte{ + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, + } + + der, err := marshalXWingPrivateKey(seed[:]) + if err != nil { + t.Fatalf("marshalXWingPrivateKey failed: %v", err) + } + + decoded, err := parseXWingPrivateKeyFromDER(der) + if err != nil { + t.Fatalf("parseXWingPrivateKeyFromDER failed: %v", err) + } + + if seed != decoded { + t.Fatal("private key round-trip mismatch") + } +} + +// TestXWingRFCTestVector1 verifies against the first test vector from +// draft-connolly-cfrg-xwing-kem-10 Appendix C. +func TestXWingRFCTestVector1(t *testing.T) { + seedHex := "7f9c2ba4e88f827d616045507605853ed73b8093f6efbc88eb1a6eacfa66ef26" + expectedSSHex := "d2df0522128f09dd8e2c92b1e905c793d8f57a54c3da25861f10bf4ca613e384" + + seed, err := hex.DecodeString(seedHex) + if err != nil { + t.Fatal(err) + } + + expectedSS, err := hex.DecodeString(expectedSSHex) + if err != nil { + t.Fatal(err) + } + + // Verify seed expands to produce the expected public key + var seedArr [32]byte + copy(seedArr[:], seed) + _, _, pkM, pkX, err := expandDecapsulationKey(seedArr) + if err != nil { + t.Fatalf("expandDecapsulationKey failed: %v", err) + } + + // The public key should be 1216 bytes + pk := make([]byte, 0, xwingPublicKeySize) + pk = append(pk, pkM...) + pk = append(pk, pkX...) + if len(pk) != xwingPublicKeySize { + t.Fatalf("public key size: got %d, want %d", len(pk), xwingPublicKeySize) + } + + // Verify the expected public key from the test vector (first 68 hex chars = 34 bytes) + expectedPKPrefix := "e2236b35a8c24b39b10aa1323a96a919a2ced88400633a7b07131713fc14b2b5" + pkHex := hex.EncodeToString(pk) + if pkHex[:64] != expectedPKPrefix { + t.Fatalf("public key prefix mismatch:\ngot %s\nwant %s", pkHex[:64], expectedPKPrefix) + } + + // We can't test encapsulate/decapsulate with the test vector directly because + // encapsulation is randomized, but we can verify the shared secret length + // and that encapsulate/decapsulate are consistent with our own keys. + ss, ct, err := xwingEncapsulate(pk) + if err != nil { + t.Fatalf("xwingEncapsulate failed: %v", err) + } + + if len(ss) != xwingSharedKeySize { + t.Fatalf("shared secret size: got %d, want %d", len(ss), xwingSharedKeySize) + } + + // Verify decapsulation recovers the same shared secret + ssDecap, err := xwingDecapsulate(ct[:], seedArr) + if err != nil { + t.Fatalf("xwingDecapsulate failed: %v", err) + } + + if ss != ssDecap { + t.Fatal("encapsulate/decapsulate shared secret mismatch") + } + + // The expected shared secret from the RFC is for a specific eseed; + // since we used a random eseed, ours will differ. Just verify the format. + _ = expectedSS // We verified the format; deterministic test would need EncapsulateDerand +} + +func TestXWingSizes(t *testing.T) { + if xwingPublicKeySize != 1216 { + t.Fatalf("xwingPublicKeySize: got %d, want 1216", xwingPublicKeySize) + } + if xwingCiphertextSize != 1120 { + t.Fatalf("xwingCiphertextSize: got %d, want 1120", xwingCiphertextSize) + } + if xwingSeedSize != 32 { + t.Fatalf("xwingSeedSize: got %d, want 32", xwingSeedSize) + } + if xwingSharedKeySize != 32 { + t.Fatalf("xwingSharedKeySize: got %d, want 32", xwingSharedKeySize) + } +} + +func TestXWingEncapsulateDecapsulateConsistency(t *testing.T) { + // Generate multiple key pairs and verify encaps/decaps consistency + for range 10 { + kp, err := NewXWingKeyPair() + if err != nil { + t.Fatalf("NewXWingKeyPair failed: %v", err) + } + + _, _, pkM, pkX, err := expandDecapsulationKey(kp.seed) + if err != nil { + t.Fatal(err) + } + + pk := make([]byte, 0, xwingPublicKeySize) + pk = append(pk, pkM...) + pk = append(pk, pkX...) + + ss, ct, err := xwingEncapsulate(pk) + if err != nil { + t.Fatalf("xwingEncapsulate failed: %v", err) + } + + ssDecap, err := xwingDecapsulate(ct[:], kp.seed) + if err != nil { + t.Fatalf("xwingDecapsulate failed: %v", err) + } + + if ss != ssDecap { + t.Fatal("encapsulate/decapsulate shared secret mismatch") + } + } +} diff --git a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go index 8e5c7b58b5..5d0d36b958 100644 --- a/protocol/go/policy/kasregistry/key_access_server_registry.pb.go +++ b/protocol/go/policy/kasregistry/key_access_server_registry.pb.go @@ -4012,218 +4012,219 @@ var file_policy_kasregistry_key_access_server_registry_proto_rawDesc = []byte{ 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, - 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xcc, 0x0c, 0x0a, 0x10, 0x43, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x3a, 0x02, 0x18, 0x01, 0x22, 0xd4, 0x0c, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1f, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, - 0x12, 0xa4, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, + 0x12, 0xac, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x6c, 0xba, 0x48, 0x69, - 0xba, 0x01, 0x66, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, + 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x74, 0xba, 0x48, 0x71, + 0xba, 0x01, 0x6e, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, - 0x1a, 0x17, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, - 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, - 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x93, 0x01, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x67, 0xba, 0x48, 0x64, - 0xba, 0x01, 0x61, 0x0a, 0x10, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x65, - 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x35, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, - 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, - 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, 0x2d, 0x34, 0x29, 0x2e, 0x1a, 0x16, 0x74, 0x68, - 0x69, 0x73, 0x20, 0x3e, 0x3d, 0x20, 0x31, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, - 0x3c, 0x3d, 0x20, 0x34, 0x52, 0x07, 0x6b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, - 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, - 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, - 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, - 0x78, 0x12, 0x3d, 0x0a, 0x0f, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, - 0x5f, 0x63, 0x74, 0x78, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x50, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, - 0x78, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, - 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, - 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 0x12, 0x16, - 0x0a, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, - 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, - 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, - 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x3a, 0xbb, 0x07, 0xba, 0x48, - 0xb7, 0x07, 0x1a, 0x97, 0x03, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, - 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, - 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xbc, 0x01, 0x54, 0x68, 0x65, - 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, - 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, - 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, - 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x2e, 0x20, - 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x69, 0x66, - 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, - 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x20, 0x6f, 0x72, 0x20, - 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, - 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0xb0, 0x01, 0x28, 0x28, 0x74, 0x68, - 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, - 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, - 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, - 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, - 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, - 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, - 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, - 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, - 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xf4, 0x02, 0x0a, - 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, - 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, 0xa8, 0x01, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x20, 0x69, 0x64, 0x20, 0x69, 0x73, 0x20, - 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, - 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, - 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, - 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, - 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, - 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, - 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, - 0x59, 0x2e, 0x1a, 0x9e, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, - 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, - 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, - 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, - 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27, - 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, - 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, - 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, - 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, - 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x20, 0x21, 0x3d, 0x20, - 0x27, 0x27, 0x29, 0x1a, 0xa3, 0x01, 0x0a, 0x23, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, - 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, - 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6f, 0x6e, 0x6c, 0x79, 0x12, 0x48, 0x70, 0x72, 0x69, - 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x20, 0x6d, 0x75, 0x73, - 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x74, 0x20, 0x69, 0x66, 0x20, - 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, - 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, - 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x32, 0x21, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, - 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x20, 0x26, 0x26, 0x20, 0x68, - 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, - 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x29, 0x29, 0x22, 0x3c, 0x0a, 0x11, 0x43, 0x72, 0x65, - 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, - 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, - 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0x7a, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4b, 0x65, - 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, + 0x1a, 0x1f, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, + 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x2c, 0x20, 0x32, 0x30, 0x2c, 0x20, 0x32, 0x31, + 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, + 0x93, 0x01, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, + 0x6f, 0x64, 0x65, 0x42, 0x67, 0xba, 0x48, 0x64, 0xba, 0x01, 0x61, 0x0a, 0x10, 0x6b, 0x65, 0x79, + 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x35, 0x54, + 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x6d, 0x75, 0x73, 0x74, + 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, + 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x20, 0x28, 0x31, + 0x2d, 0x34, 0x29, 0x2e, 0x1a, 0x16, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3e, 0x3d, 0x20, 0x31, 0x20, + 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x3c, 0x3d, 0x20, 0x34, 0x52, 0x07, 0x6b, 0x65, + 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x42, 0x0a, 0x0e, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, + 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, + 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, + 0x43, 0x74, 0x78, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x0c, 0x70, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x3d, 0x0a, 0x0f, 0x70, 0x72, 0x69, + 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x72, 0x69, 0x76, + 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x52, 0x0d, 0x70, 0x72, 0x69, 0x76, 0x61, + 0x74, 0x65, 0x4b, 0x65, 0x79, 0x43, 0x74, 0x78, 0x12, 0x2c, 0x0a, 0x12, 0x70, 0x72, 0x6f, 0x76, + 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x43, 0x6f, + 0x6e, 0x66, 0x69, 0x67, 0x49, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x12, 0x33, + 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x3a, 0xbb, 0x07, 0xba, 0x48, 0xb7, 0x07, 0x1a, 0x97, 0x03, 0x0a, 0x23, 0x70, + 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x64, 0x12, 0xbc, 0x01, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, + 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, + 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, + 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, + 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, + 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, + 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x77, 0x72, 0x61, 0x70, + 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, + 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, + 0x65, 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, + 0x4d, 0x4f, 0x54, 0x45, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, + 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, + 0x2e, 0x1a, 0xb0, 0x01, 0x28, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x32, 0x29, 0x20, + 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, + 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, + 0x6b, 0x65, 0x79, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, + 0x20, 0x33, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, + 0x78, 0x2e, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x64, 0x5f, 0x6b, 0x65, 0x79, 0x20, 0x3d, 0x3d, + 0x20, 0x27, 0x27, 0x29, 0x1a, 0xf4, 0x02, 0x0a, 0x26, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x69, 0x64, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x61, 0x6c, 0x6c, 0x79, 0x5f, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, 0x12, + 0xa8, 0x01, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x20, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x20, 0x69, 0x64, 0x20, 0x69, 0x73, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x64, + 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x69, 0x73, 0x20, + 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, + 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x6f, 0x72, 0x20, 0x4b, 0x45, + 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x2e, 0x20, 0x49, + 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x20, + 0x66, 0x6f, 0x72, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, + 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, + 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x9e, 0x01, 0x28, 0x28, 0x74, + 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, + 0x31, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, + 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x34, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, + 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x5f, 0x69, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x28, + 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, + 0x20, 0x32, 0x20, 0x7c, 0x7c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, + 0x6f, 0x64, 0x65, 0x20, 0x3d, 0x3d, 0x20, 0x33, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, + 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x5f, 0x69, 0x64, 0x20, 0x21, 0x3d, 0x20, 0x27, 0x27, 0x29, 0x1a, 0xa3, 0x01, 0x0a, 0x23, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x5f, + 0x66, 0x6f, 0x72, 0x5f, 0x70, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x6f, + 0x6e, 0x6c, 0x79, 0x12, 0x48, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, + 0x5f, 0x63, 0x74, 0x78, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, + 0x20, 0x73, 0x65, 0x74, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, + 0x20, 0x69, 0x73, 0x20, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, + 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x2e, 0x1a, 0x32, 0x21, + 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x20, 0x3d, + 0x3d, 0x20, 0x34, 0x20, 0x26, 0x26, 0x20, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, + 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x74, 0x78, 0x29, + 0x29, 0x22, 0x3c, 0x0a, 0x11, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, + 0x7a, 0x0a, 0x0d, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, + 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, + 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, + 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, + 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0x39, 0x0a, 0x0e, 0x47, + 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, + 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, + 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0x96, 0x03, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, + 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0xa7, 0x01, 0x0a, 0x0d, 0x6b, + 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, + 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x6f, 0xba, 0x48, 0x6c, 0xba, 0x01, 0x69, 0x0a, 0x15, 0x6b, + 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, + 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, + 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x1a, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x20, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, + 0x20, 0x34, 0x2c, 0x20, 0x35, 0x5d, 0x52, 0x0c, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, + 0x69, 0x74, 0x68, 0x6d, 0x12, 0x21, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, - 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, - 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x42, 0x13, - 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, - 0x02, 0x08, 0x01, 0x22, 0x39, 0x0a, 0x0e, 0x47, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0x96, - 0x03, 0x0a, 0x0f, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0xa7, 0x01, 0x0a, 0x0d, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, - 0x69, 0x74, 0x68, 0x6d, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x6f, 0xba, - 0x48, 0x6c, 0xba, 0x01, 0x69, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, - 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, - 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x2e, 0x1a, 0x1a, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x30, 0x2c, 0x20, - 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x5d, 0x52, 0x0c, - 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x21, 0x0a, 0x06, - 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, - 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, - 0x24, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x07, 0x6b, 0x61, - 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, - 0x01, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x55, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x06, - 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, - 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x88, 0x01, 0x01, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, - 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0c, - 0x0a, 0x0a, 0x6b, 0x61, 0x73, 0x5f, 0x66, 0x69, 0x6c, 0x74, 0x65, 0x72, 0x42, 0x09, 0x0a, 0x07, - 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x22, 0x73, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4b, - 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x08, 0x6b, - 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, - 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x6b, - 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x34, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x86, 0x03, 0x0a, - 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, - 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, 0x02, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, - 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, - 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, - 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, - 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, - 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x18, 0x65, 0x20, 0x01, - 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x16, - 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x65, - 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x3a, 0xcc, 0x01, 0xba, 0x48, 0xc8, 0x01, 0x1a, 0xc5, 0x01, - 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, - 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x12, 0x52, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x20, 0x62, 0x65, 0x68, 0x61, - 0x76, 0x69, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x69, 0x74, - 0x68, 0x65, 0x72, 0x20, 0x41, 0x50, 0x50, 0x45, 0x4e, 0x44, 0x20, 0x6f, 0x72, 0x20, 0x52, 0x45, - 0x50, 0x4c, 0x41, 0x43, 0x45, 0x2c, 0x20, 0x77, 0x68, 0x65, 0x6e, 0x20, 0x75, 0x70, 0x64, 0x61, - 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x1a, 0x55, - 0x28, 0x28, 0x21, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0x29, 0x29, 0x20, 0x7c, 0x7c, 0x20, 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, - 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x26, 0x26, - 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x20, 0x21, - 0x3d, 0x20, 0x30, 0x29, 0x29, 0x22, 0x3c, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, - 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, - 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, - 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, - 0x4b, 0x65, 0x79, 0x22, 0xa4, 0x01, 0x0a, 0x10, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, - 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x21, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, - 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, - 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x04, 0x6e, + 0x52, 0x05, 0x6b, 0x61, 0x73, 0x49, 0x64, 0x12, 0x24, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, - 0x10, 0x01, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x03, 0x75, 0x72, - 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, - 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x19, 0x0a, 0x03, 0x6b, 0x69, - 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, - 0x52, 0x03, 0x6b, 0x69, 0x64, 0x42, 0x13, 0x0a, 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, - 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, 0x08, 0x01, 0x22, 0xe5, 0x0e, 0x0a, 0x10, 0x52, - 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, - 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, - 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, - 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, - 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, - 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, 0x07, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, - 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, - 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4e, 0x65, 0x77, - 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x1a, 0xcf, 0x04, 0x0a, 0x06, - 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x12, 0x1e, 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x9d, 0x01, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, - 0x69, 0x74, 0x68, 0x6d, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, - 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x6c, 0xba, - 0x48, 0x69, 0xba, 0x01, 0x66, 0x0a, 0x15, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, - 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, - 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, - 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, - 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x73, 0x2e, 0x1a, 0x17, 0x74, 0x68, 0x69, 0x73, 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, - 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, 0x20, 0x35, 0x5d, 0x52, 0x09, 0x61, 0x6c, 0x67, + 0x10, 0x01, 0x48, 0x00, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x25, 0x0a, + 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, 0x0a, + 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x06, 0x6b, 0x61, + 0x73, 0x55, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x18, 0x08, + 0x20, 0x01, 0x28, 0x08, 0x48, 0x01, 0x52, 0x06, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x88, 0x01, + 0x01, 0x12, 0x33, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, + 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, + 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, + 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x42, 0x0c, 0x0a, 0x0a, 0x6b, 0x61, 0x73, 0x5f, 0x66, 0x69, + 0x6c, 0x74, 0x65, 0x72, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x6c, 0x65, 0x67, 0x61, 0x63, 0x79, 0x22, + 0x73, 0x0a, 0x10, 0x4c, 0x69, 0x73, 0x74, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x29, 0x0a, 0x08, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x73, 0x18, + 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, + 0x61, 0x73, 0x4b, 0x65, 0x79, 0x52, 0x07, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x34, + 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x0a, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x50, 0x61, 0x67, 0x65, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x86, 0x03, 0x0a, 0x10, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, + 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x02, 0x69, 0x64, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x33, 0x0a, 0x08, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x18, + 0x64, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x75, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x52, 0x08, + 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x54, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, + 0x76, 0x69, 0x6f, 0x72, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1a, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x45, 0x6e, 0x75, 0x6d, 0x52, 0x16, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x42, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x3a, 0xcc, + 0x01, 0xba, 0x48, 0xc8, 0x01, 0x1a, 0xc5, 0x01, 0x0a, 0x18, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, + 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, + 0x6f, 0x72, 0x12, 0x52, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x20, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x20, 0x62, 0x65, 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x20, 0x6d, 0x75, 0x73, + 0x74, 0x20, 0x62, 0x65, 0x20, 0x65, 0x69, 0x74, 0x68, 0x65, 0x72, 0x20, 0x41, 0x50, 0x50, 0x45, + 0x4e, 0x44, 0x20, 0x6f, 0x72, 0x20, 0x52, 0x45, 0x50, 0x4c, 0x41, 0x43, 0x45, 0x2c, 0x20, 0x77, + 0x68, 0x65, 0x6e, 0x20, 0x75, 0x70, 0x64, 0x61, 0x74, 0x69, 0x6e, 0x67, 0x20, 0x6d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x1a, 0x55, 0x28, 0x28, 0x21, 0x68, 0x61, 0x73, 0x28, 0x74, + 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x29, 0x29, 0x20, 0x7c, + 0x7c, 0x20, 0x28, 0x68, 0x61, 0x73, 0x28, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, 0x74, 0x61, + 0x64, 0x61, 0x74, 0x61, 0x29, 0x20, 0x26, 0x26, 0x20, 0x74, 0x68, 0x69, 0x73, 0x2e, 0x6d, 0x65, + 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x62, 0x65, + 0x68, 0x61, 0x76, 0x69, 0x6f, 0x72, 0x20, 0x21, 0x3d, 0x20, 0x30, 0x29, 0x29, 0x22, 0x3c, 0x0a, + 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x27, 0x0a, 0x07, 0x6b, 0x61, 0x73, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x61, 0x73, + 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x22, 0xa4, 0x01, 0x0a, 0x10, + 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, + 0x12, 0x21, 0x0a, 0x06, 0x6b, 0x61, 0x73, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, 0x05, 0x6b, 0x61, + 0x73, 0x49, 0x64, 0x12, 0x1d, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x00, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x03, 0x75, 0x72, 0x69, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x0a, 0xba, 0x48, 0x07, 0x72, 0x05, 0x10, 0x01, 0x88, 0x01, 0x01, 0x48, 0x00, 0x52, 0x03, 0x75, + 0x72, 0x69, 0x12, 0x19, 0x0a, 0x03, 0x6b, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x6b, 0x69, 0x64, 0x42, 0x13, 0x0a, + 0x0a, 0x69, 0x64, 0x65, 0x6e, 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x12, 0x05, 0xba, 0x48, 0x02, + 0x08, 0x01, 0x22, 0xed, 0x0e, 0x0a, 0x10, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1a, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x08, 0xba, 0x48, 0x05, 0x72, 0x03, 0xb0, 0x01, 0x01, 0x48, 0x00, 0x52, + 0x02, 0x69, 0x64, 0x12, 0x38, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x24, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x4b, 0x61, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x65, 0x6e, + 0x74, 0x69, 0x66, 0x69, 0x65, 0x72, 0x48, 0x00, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x44, 0x0a, + 0x07, 0x6e, 0x65, 0x77, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x2b, + 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x6b, 0x61, 0x73, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x72, 0x79, 0x2e, 0x52, 0x6f, 0x74, 0x61, 0x74, 0x65, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x2e, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x52, 0x06, 0x6e, 0x65, 0x77, + 0x4b, 0x65, 0x79, 0x1a, 0xd7, 0x04, 0x0a, 0x06, 0x4e, 0x65, 0x77, 0x4b, 0x65, 0x79, 0x12, 0x1e, + 0x0a, 0x06, 0x6b, 0x65, 0x79, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x05, 0x6b, 0x65, 0x79, 0x49, 0x64, 0x12, 0xa5, + 0x01, 0x0a, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x0e, 0x32, 0x11, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x41, 0x6c, 0x67, 0x6f, + 0x72, 0x69, 0x74, 0x68, 0x6d, 0x42, 0x74, 0xba, 0x48, 0x71, 0xba, 0x01, 0x6e, 0x0a, 0x15, 0x6b, + 0x65, 0x79, 0x5f, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x5f, 0x64, 0x65, 0x66, + 0x69, 0x6e, 0x65, 0x64, 0x12, 0x34, 0x54, 0x68, 0x65, 0x20, 0x6b, 0x65, 0x79, 0x5f, 0x61, 0x6c, + 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62, 0x65, 0x20, + 0x6f, 0x6e, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x74, 0x68, 0x65, 0x20, 0x64, 0x65, 0x66, 0x69, 0x6e, + 0x65, 0x64, 0x20, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x73, 0x2e, 0x1a, 0x1f, 0x74, 0x68, 0x69, 0x73, + 0x20, 0x69, 0x6e, 0x20, 0x5b, 0x31, 0x2c, 0x20, 0x32, 0x2c, 0x20, 0x33, 0x2c, 0x20, 0x34, 0x2c, + 0x20, 0x35, 0x2c, 0x20, 0x32, 0x30, 0x2c, 0x20, 0x32, 0x31, 0x5d, 0x52, 0x09, 0x61, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x9e, 0x01, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x5f, 0x6d, 0x6f, 0x64, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x2e, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x42, 0x72, 0xba, 0x48, 0x6f, 0xba, diff --git a/protocol/go/policy/objects.pb.go b/protocol/go/policy/objects.pb.go index d2c0921c8b..2675402efc 100644 --- a/protocol/go/policy/objects.pb.go +++ b/protocol/go/policy/objects.pb.go @@ -243,17 +243,23 @@ const ( KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 KasPublicKeyAlgEnum = 5 KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 KasPublicKeyAlgEnum = 6 KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 KasPublicKeyAlgEnum = 7 + KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 KasPublicKeyAlgEnum = 20 + KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 KasPublicKeyAlgEnum = 21 + KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_XWING KasPublicKeyAlgEnum = 30 ) // Enum value maps for KasPublicKeyAlgEnum. var ( KasPublicKeyAlgEnum_name = map[int32]string{ - 0: "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED", - 1: "KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048", - 2: "KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096", - 5: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1", - 6: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1", - 7: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1", + 0: "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED", + 1: "KAS_PUBLIC_KEY_ALG_ENUM_RSA_2048", + 2: "KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096", + 5: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1", + 6: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1", + 7: "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1", + 20: "KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768", + 21: "KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024", + 30: "KAS_PUBLIC_KEY_ALG_ENUM_XWING", } KasPublicKeyAlgEnum_value = map[string]int32{ "KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED": 0, @@ -262,6 +268,9 @@ var ( "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1": 5, "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1": 6, "KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1": 7, + "KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768": 20, + "KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024": 21, + "KAS_PUBLIC_KEY_ALG_ENUM_XWING": 30, } ) @@ -302,17 +311,23 @@ const ( Algorithm_ALGORITHM_EC_P256 Algorithm = 3 Algorithm_ALGORITHM_EC_P384 Algorithm = 4 Algorithm_ALGORITHM_EC_P521 Algorithm = 5 + Algorithm_ALGORITHM_MLKEM_768 Algorithm = 20 + Algorithm_ALGORITHM_MLKEM_1024 Algorithm = 21 + Algorithm_ALGORITHM_XWING Algorithm = 30 ) // Enum value maps for Algorithm. var ( Algorithm_name = map[int32]string{ - 0: "ALGORITHM_UNSPECIFIED", - 1: "ALGORITHM_RSA_2048", - 2: "ALGORITHM_RSA_4096", - 3: "ALGORITHM_EC_P256", - 4: "ALGORITHM_EC_P384", - 5: "ALGORITHM_EC_P521", + 0: "ALGORITHM_UNSPECIFIED", + 1: "ALGORITHM_RSA_2048", + 2: "ALGORITHM_RSA_4096", + 3: "ALGORITHM_EC_P256", + 4: "ALGORITHM_EC_P384", + 5: "ALGORITHM_EC_P521", + 20: "ALGORITHM_MLKEM_768", + 21: "ALGORITHM_MLKEM_1024", + 30: "ALGORITHM_XWING", } Algorithm_value = map[string]int32{ "ALGORITHM_UNSPECIFIED": 0, @@ -321,6 +336,9 @@ var ( "ALGORITHM_EC_P256": 3, "ALGORITHM_EC_P384": 4, "ALGORITHM_EC_P521": 5, + "ALGORITHM_MLKEM_768": 20, + "ALGORITHM_MLKEM_1024": 21, + "ALGORITHM_XWING": 30, } ) @@ -3697,7 +3715,7 @@ var file_policy_objects_proto_rawDesc = []byte{ 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x49, 0x4e, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x01, 0x12, 0x18, 0x0a, 0x14, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x58, 0x54, 0x45, 0x52, 0x4e, 0x41, 0x4c, 0x10, 0x02, - 0x2a, 0x88, 0x02, 0x0a, 0x13, 0x4b, 0x61, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, + 0x2a, 0xfa, 0x02, 0x0a, 0x13, 0x4b, 0x61, 0x73, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x67, 0x45, 0x6e, 0x75, 0x6d, 0x12, 0x27, 0x0a, 0x23, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, @@ -3713,41 +3731,52 @@ var file_policy_objects_proto_rawDesc = []byte{ 0x55, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x53, 0x45, 0x43, 0x50, 0x33, 0x38, 0x34, 0x52, 0x31, 0x10, 0x06, 0x12, 0x28, 0x0a, 0x24, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x45, 0x43, 0x5f, - 0x53, 0x45, 0x43, 0x50, 0x35, 0x32, 0x31, 0x52, 0x31, 0x10, 0x07, 0x2a, 0x9b, 0x01, 0x0a, 0x09, - 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x19, 0x0a, 0x15, 0x41, 0x4c, 0x47, - 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, - 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, - 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, - 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x34, 0x30, - 0x39, 0x36, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, - 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x32, 0x35, 0x36, 0x10, 0x03, 0x12, 0x15, 0x0a, 0x11, 0x41, - 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x33, 0x38, 0x34, - 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, - 0x45, 0x43, 0x5f, 0x50, 0x35, 0x32, 0x31, 0x10, 0x05, 0x2a, 0x56, 0x0a, 0x09, 0x4b, 0x65, 0x79, - 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x1a, 0x0a, 0x16, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, - 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, - 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, - 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x4b, 0x45, 0x59, - 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x52, 0x4f, 0x54, 0x41, 0x54, 0x45, 0x44, 0x10, - 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x07, 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, - 0x14, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, - 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x4b, 0x45, 0x59, 0x5f, 0x4d, - 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, - 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x1e, 0x0a, 0x1a, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, - 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, 0x13, 0x0a, 0x0f, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, - 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, 0x54, 0x45, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x4b, 0x45, - 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, - 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, 0x10, 0x04, 0x42, 0x82, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, - 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x42, 0x0c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, - 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, - 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, - 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, - 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x06, - 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xca, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xe2, - 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, - 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x62, 0x06, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x53, 0x45, 0x43, 0x50, 0x35, 0x32, 0x31, 0x52, 0x31, 0x10, 0x07, 0x12, 0x25, 0x0a, 0x21, 0x4b, + 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, + 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x5f, 0x37, 0x36, 0x38, + 0x10, 0x14, 0x12, 0x26, 0x0a, 0x22, 0x4b, 0x41, 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, + 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x4d, 0x4c, + 0x4b, 0x45, 0x4d, 0x5f, 0x31, 0x30, 0x32, 0x34, 0x10, 0x15, 0x12, 0x21, 0x0a, 0x1d, 0x4b, 0x41, + 0x53, 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x41, 0x4c, 0x47, + 0x5f, 0x45, 0x4e, 0x55, 0x4d, 0x5f, 0x58, 0x57, 0x49, 0x4e, 0x47, 0x10, 0x1e, 0x2a, 0xe3, 0x01, + 0x0a, 0x09, 0x41, 0x6c, 0x67, 0x6f, 0x72, 0x69, 0x74, 0x68, 0x6d, 0x12, 0x19, 0x0a, 0x15, 0x41, + 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, + 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x16, 0x0a, 0x12, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, + 0x54, 0x48, 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, 0x32, 0x30, 0x34, 0x38, 0x10, 0x01, 0x12, 0x16, + 0x0a, 0x12, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x52, 0x53, 0x41, 0x5f, + 0x34, 0x30, 0x39, 0x36, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, + 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x32, 0x35, 0x36, 0x10, 0x03, 0x12, 0x15, 0x0a, + 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x33, + 0x38, 0x34, 0x10, 0x04, 0x12, 0x15, 0x0a, 0x11, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, + 0x4d, 0x5f, 0x45, 0x43, 0x5f, 0x50, 0x35, 0x32, 0x31, 0x10, 0x05, 0x12, 0x17, 0x0a, 0x13, 0x41, + 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x5f, 0x37, + 0x36, 0x38, 0x10, 0x14, 0x12, 0x18, 0x0a, 0x14, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, + 0x4d, 0x5f, 0x4d, 0x4c, 0x4b, 0x45, 0x4d, 0x5f, 0x31, 0x30, 0x32, 0x34, 0x10, 0x15, 0x12, 0x13, + 0x0a, 0x0f, 0x41, 0x4c, 0x47, 0x4f, 0x52, 0x49, 0x54, 0x48, 0x4d, 0x5f, 0x58, 0x57, 0x49, 0x4e, + 0x47, 0x10, 0x1e, 0x2a, 0x56, 0x0a, 0x09, 0x4b, 0x65, 0x79, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x12, 0x1a, 0x0a, 0x16, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x55, + 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x15, 0x0a, 0x11, + 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, + 0x45, 0x10, 0x01, 0x12, 0x16, 0x0a, 0x12, 0x4b, 0x45, 0x59, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x55, + 0x53, 0x5f, 0x52, 0x4f, 0x54, 0x41, 0x54, 0x45, 0x44, 0x10, 0x02, 0x2a, 0x94, 0x01, 0x0a, 0x07, + 0x4b, 0x65, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x18, 0x0a, 0x14, 0x4b, 0x45, 0x59, 0x5f, 0x4d, + 0x4f, 0x44, 0x45, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, + 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x43, 0x4f, + 0x4e, 0x46, 0x49, 0x47, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, + 0x1e, 0x0a, 0x1a, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x50, 0x52, 0x4f, 0x56, + 0x49, 0x44, 0x45, 0x52, 0x5f, 0x52, 0x4f, 0x4f, 0x54, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x02, 0x12, + 0x13, 0x0a, 0x0f, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, 0x5f, 0x52, 0x45, 0x4d, 0x4f, + 0x54, 0x45, 0x10, 0x03, 0x12, 0x1c, 0x0a, 0x18, 0x4b, 0x45, 0x59, 0x5f, 0x4d, 0x4f, 0x44, 0x45, + 0x5f, 0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x4f, 0x4e, 0x4c, 0x59, + 0x10, 0x04, 0x42, 0x82, 0x01, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x2e, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0x42, 0x0c, 0x4f, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, + 0x01, 0x5a, 0x2e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6f, 0x70, + 0x65, 0x6e, 0x74, 0x64, 0x66, 0x2f, 0x70, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x2f, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x2f, 0x67, 0x6f, 0x2f, 0x70, 0x6f, 0x6c, 0x69, 0x63, + 0x79, 0xa2, 0x02, 0x03, 0x50, 0x58, 0x58, 0xaa, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, + 0xca, 0x02, 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0xe2, 0x02, 0x12, 0x50, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, + 0x06, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/sdk/basekey.go b/sdk/basekey.go index 5f3cc12004..2f159ba12d 100644 --- a/sdk/basekey.go +++ b/sdk/basekey.go @@ -33,6 +33,12 @@ func getKasKeyAlg(alg string) policy.Algorithm { return policy.Algorithm_ALGORITHM_EC_P384 case string(ocrypto.EC521Key): return policy.Algorithm_ALGORITHM_EC_P521 + case string(ocrypto.MLKEM768Key): + return policy.Algorithm_ALGORITHM_MLKEM_768 + case string(ocrypto.MLKEM1024Key): + return policy.Algorithm_ALGORITHM_MLKEM_1024 + case string(ocrypto.HybridXWing): + return policy.Algorithm_ALGORITHM_XWING default: return policy.Algorithm_ALGORITHM_UNSPECIFIED } @@ -51,6 +57,12 @@ func formatAlg(alg policy.Algorithm) (string, error) { return string(ocrypto.EC384Key), nil case policy.Algorithm_ALGORITHM_EC_P521: return string(ocrypto.EC521Key), nil + case policy.Algorithm_ALGORITHM_MLKEM_768: + return string(ocrypto.MLKEM768Key), nil + case policy.Algorithm_ALGORITHM_MLKEM_1024: + return string(ocrypto.MLKEM1024Key), nil + case policy.Algorithm_ALGORITHM_XWING: + return string(ocrypto.HybridXWing), nil case policy.Algorithm_ALGORITHM_UNSPECIFIED: fallthrough default: diff --git a/sdk/codegen/runner/generate.go b/sdk/codegen/runner/generate.go index 211cedfdc4..0a8a88e197 100644 --- a/sdk/codegen/runner/generate.go +++ b/sdk/codegen/runner/generate.go @@ -177,12 +177,11 @@ func New%s%s%sConnectWrapper(httpClient connect.HTTPClient, baseURL string, opts func generateInterfaceType(interfaceName string, methods []string, packageName, prefix, suffix string) string { // Generate the interface type definition var builder strings.Builder - builder.WriteString(fmt.Sprintf(` + fmt.Fprintf(&builder, ` type %s%s%s interface { -`, prefix, interfaceName, suffix)) +`, prefix, interfaceName, suffix) for _, method := range methods { - builder.WriteString(fmt.Sprintf(` %s(ctx context.Context, req *%s.%sRequest) (*%s.%sResponse, error) -`, method, packageName, method, packageName, method)) + fmt.Fprintf(&builder, " %s(ctx context.Context, req *%s.%sRequest) (*%s.%sResponse, error)\n", method, packageName, method, packageName, method) } builder.WriteString("}\n") return builder.String() diff --git a/sdk/experimental/tdf/keysplit/attributes.go b/sdk/experimental/tdf/keysplit/attributes.go index e12d838e97..95d30e4da6 100644 --- a/sdk/experimental/tdf/keysplit/attributes.go +++ b/sdk/experimental/tdf/keysplit/attributes.go @@ -199,6 +199,12 @@ func formatAlgorithm(alg policy.Algorithm) string { return "rsa:2048" case policy.Algorithm_ALGORITHM_RSA_4096: return "rsa:4096" + case policy.Algorithm_ALGORITHM_MLKEM_768: + return "mlkem:768" + case policy.Algorithm_ALGORITHM_MLKEM_1024: + return "mlkem:1024" + case policy.Algorithm_ALGORITHM_XWING: + return "hpqt:xwing" default: return unknownAlgorithm } @@ -217,6 +223,12 @@ func convertAlgEnum2Simple(a policy.KasPublicKeyAlgEnum) policy.Algorithm { return policy.Algorithm_ALGORITHM_RSA_2048 case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096: return policy.Algorithm_ALGORITHM_RSA_4096 + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768: + return policy.Algorithm_ALGORITHM_MLKEM_768 + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024: + return policy.Algorithm_ALGORITHM_MLKEM_1024 + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_XWING: + return policy.Algorithm_ALGORITHM_XWING case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED: return policy.Algorithm_ALGORITHM_UNSPECIFIED default: diff --git a/sdk/granter.go b/sdk/granter.go index 8275a04f42..a50f6299fd 100644 --- a/sdk/granter.go +++ b/sdk/granter.go @@ -286,6 +286,12 @@ func convertAlgEnum2Simple(a policy.KasPublicKeyAlgEnum) policy.Algorithm { return policy.Algorithm_ALGORITHM_RSA_2048 case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096: return policy.Algorithm_ALGORITHM_RSA_4096 + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768: + return policy.Algorithm_ALGORITHM_MLKEM_768 + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024: + return policy.Algorithm_ALGORITHM_MLKEM_1024 + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_XWING: + return policy.Algorithm_ALGORITHM_XWING case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED: return policy.Algorithm_ALGORITHM_UNSPECIFIED default: @@ -306,6 +312,12 @@ func convertStringToAlgorithm(alg string) policy.Algorithm { return policy.Algorithm_ALGORITHM_RSA_2048 case ocrypto.RSA4096Key: return policy.Algorithm_ALGORITHM_RSA_4096 + case ocrypto.MLKEM768Key: + return policy.Algorithm_ALGORITHM_MLKEM_768 + case ocrypto.MLKEM1024Key: + return policy.Algorithm_ALGORITHM_MLKEM_1024 + case ocrypto.HybridXWing: + return policy.Algorithm_ALGORITHM_XWING default: return policy.Algorithm_ALGORITHM_UNSPECIFIED } @@ -490,6 +502,12 @@ func algProto2String(e policy.KasPublicKeyAlgEnum) string { return string(ocrypto.RSA2048Key) case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_RSA_4096: return string(ocrypto.RSA4096Key) + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768: + return string(ocrypto.MLKEM768Key) + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024: + return string(ocrypto.MLKEM1024Key) + case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_XWING: + return string(ocrypto.HybridXWing) case policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED: return "" } @@ -508,6 +526,12 @@ func algProto2OcryptoKeyType(e policy.Algorithm) ocrypto.KeyType { return ocrypto.RSA2048Key case policy.Algorithm_ALGORITHM_RSA_4096: return ocrypto.RSA4096Key + case policy.Algorithm_ALGORITHM_MLKEM_768: + return ocrypto.MLKEM768Key + case policy.Algorithm_ALGORITHM_MLKEM_1024: + return ocrypto.MLKEM1024Key + case policy.Algorithm_ALGORITHM_XWING: + return ocrypto.HybridXWing case policy.Algorithm_ALGORITHM_UNSPECIFIED: return ocrypto.KeyType("") default: diff --git a/sdk/tdf.go b/sdk/tdf.go index db22208b5f..760677e3e9 100644 --- a/sdk/tdf.go +++ b/sdk/tdf.go @@ -43,6 +43,8 @@ const ( kKeySize = 32 kWrapped = "wrapped" kECWrapped = "ec-wrapped" + kMLKEMWrapped = "mlkem-wrapped" + kHybrid = "hybrid" kKasProtocol = "kas" kSplitKeyType = "split" kGCMCipherAlgorithm = "AES-256-GCM" @@ -674,7 +676,8 @@ func createKeyAccess(kasInfo KASInfo, symKey []byte, policyBinding PolicyBinding } ktype := ocrypto.KeyType(kasInfo.Algorithm) - if ocrypto.IsECKeyType(ktype) { + switch { + case ocrypto.IsECKeyType(ktype): mode, err := ocrypto.ECKeyTypeToMode(ktype) if err != nil { return KeyAccess{}, err @@ -686,7 +689,23 @@ func createKeyAccess(kasInfo KASInfo, symKey []byte, policyBinding PolicyBinding keyAccess.KeyType = kECWrapped keyAccess.WrappedKey = wrappedKeyInfo.wrappedKey keyAccess.EphemeralPublicKey = wrappedKeyInfo.publicKey - } else { + case ktype == ocrypto.MLKEM768Key, ktype == ocrypto.MLKEM1024Key: + wrappedKey, encapsulatedKey, err := generateWrapKeyWithMLKEM(kasInfo.PublicKey, symKey) + if err != nil { + return KeyAccess{}, err + } + keyAccess.KeyType = kMLKEMWrapped + keyAccess.WrappedKey = wrappedKey + keyAccess.EphemeralPublicKey = encapsulatedKey + case ktype == ocrypto.HybridXWing: + wrappedKey, encapsulatedKey, err := generateWrapKeyWithMLKEM(kasInfo.PublicKey, symKey) + if err != nil { + return KeyAccess{}, err + } + keyAccess.KeyType = kHybrid + keyAccess.WrappedKey = wrappedKey + keyAccess.EphemeralPublicKey = encapsulatedKey + default: wrappedKey, err := generateWrapKeyWithRSA(kasInfo.PublicKey, symKey) if err != nil { return KeyAccess{}, err @@ -761,6 +780,25 @@ func generateWrapKeyWithRSA(publicKey string, symKey []byte) (string, error) { return string(ocrypto.Base64Encode(wrappedKey)), nil } +func generateWrapKeyWithMLKEM(publicKey string, symKey []byte) (string, string, error) { + publicKeyEncryptor, err := ocrypto.FromPublicPEM(publicKey) + if err != nil { + return "", "", fmt.Errorf("generateWrapKeyWithMLKEM: ocrypto.FromPublicPEM failed:%w", err) + } + + wrappedKey, err := publicKeyEncryptor.Encrypt(symKey) + if err != nil { + return "", "", fmt.Errorf("generateWrapKeyWithMLKEM: encrypt failed:%w", err) + } + + encapsulatedKey := publicKeyEncryptor.EphemeralKey() + if len(encapsulatedKey) == 0 { + return "", "", errors.New("generateWrapKeyWithMLKEM: encapsulated key missing") + } + + return string(ocrypto.Base64Encode(wrappedKey)), string(ocrypto.Base64Encode(encapsulatedKey)), nil +} + // create policy object func createPolicyObject(attributes []AttributeValueFQN) (PolicyObject, error) { uuidObj, err := uuid.NewUUID() diff --git a/service/internal/security/basic_manager.go b/service/internal/security/basic_manager.go index 6197e50eb3..78ba5b92e5 100644 --- a/service/internal/security/basic_manager.go +++ b/service/internal/security/basic_manager.go @@ -97,6 +97,26 @@ func (b *BasicManager) Decrypt(ctx context.Context, keyDetails trust.KeyDetails, return nil, fmt.Errorf("failed to create protected key: %w", err) } return protectedKey, nil + case ocrypto.MLKEM768Key, ocrypto.MLKEM1024Key: + plaintext, err := decrypter.DecryptWithEphemeralKey(ciphertext, ephemeralPublicKey) + if err != nil { + return nil, fmt.Errorf("failed to decrypt with ML-KEM: %w", err) + } + protectedKey, err := ocrypto.NewAESProtectedKey(plaintext) + if err != nil { + return nil, fmt.Errorf("failed to create protected key: %w", err) + } + return protectedKey, nil + case ocrypto.HybridXWing: + plaintext, err := decrypter.DecryptWithEphemeralKey(ciphertext, ephemeralPublicKey) + if err != nil { + return nil, fmt.Errorf("failed to decrypt with X-Wing hybrid: %w", err) + } + protectedKey, err := ocrypto.NewAESProtectedKey(plaintext) + if err != nil { + return nil, fmt.Errorf("failed to create protected key: %w", err) + } + return protectedKey, nil } return nil, fmt.Errorf("unsupported algorithm: %s", keyDetails.Algorithm()) diff --git a/service/internal/security/crypto_provider.go b/service/internal/security/crypto_provider.go index 24f0f3c2ea..8640bbdd97 100644 --- a/service/internal/security/crypto_provider.go +++ b/service/internal/security/crypto_provider.go @@ -11,4 +11,11 @@ const ( // Used for encryption with RSA of the KAO AlgorithmRSA2048 = "rsa:2048" AlgorithmRSA4096 = "rsa:4096" + + // Used for encryption with ML-KEM of the KAO + AlgorithmMLKEM768 = "mlkem:768" + AlgorithmMLKEM1024 = "mlkem:1024" + + // Used for hybrid PQ/T encryption with X-Wing (X25519 + ML-KEM-768) + AlgorithmXWing = "hpqt:xwing" ) diff --git a/service/internal/security/in_process_provider.go b/service/internal/security/in_process_provider.go index 67c86487aa..619315ce6c 100644 --- a/service/internal/security/in_process_provider.go +++ b/service/internal/security/in_process_provider.go @@ -2,7 +2,6 @@ package security import ( "context" - "crypto" "crypto/elliptic" "crypto/x509" "encoding/pem" @@ -80,11 +79,7 @@ func (k *KeyDetailsAdapter) ExportPublicKey(_ context.Context, format trust.KeyT return jwkKey, nil case trust.KeyTypePKCS8: - // Try to get the key as an RSA key first - if rsaKey, err := k.cryptoProvider.RSAPublicKey(kid); err == nil { - return rsaKey, nil - } - return k.cryptoProvider.ECPublicKey(kid) + return k.cryptoProvider.PublicKey(kid) default: return "", ErrCertNotFound } @@ -92,8 +87,11 @@ func (k *KeyDetailsAdapter) ExportPublicKey(_ context.Context, format trust.KeyT func (k *KeyDetailsAdapter) ExportCertificate(_ context.Context) (string, error) { kid := string(k.id) - if k.algorithm == AlgorithmECP256R1 { + switch k.algorithm { + case AlgorithmECP256R1, AlgorithmECP384R1, AlgorithmECP521R1: return k.cryptoProvider.ECCertificate(kid) + case ocrypto.RSA2048Key, ocrypto.RSA4096Key, ocrypto.MLKEM768Key, ocrypto.MLKEM1024Key, ocrypto.HybridXWing: + return "", errors.New("certificates only available for EC keys") } return "", errors.New("certificates only available for EC keys") } @@ -170,31 +168,29 @@ func (a *InProcessProvider) FindKeyByAlgorithm(_ context.Context, algorithm stri // FindKeyByID finds a key by ID func (a *InProcessProvider) FindKeyByID(_ context.Context, id trust.KeyIdentifier) (trust.KeyDetails, error) { - // Try to determine the algorithm by checking if the key works with known algorithms - for _, alg := range []string{AlgorithmECP256R1, AlgorithmRSA2048} { - // This is a hack since the original provider doesn't have a way to check if a key exists - switch alg { - case AlgorithmECP256R1: - if _, err := a.cryptoProvider.ECPublicKey(string(id)); err == nil { - return &KeyDetailsAdapter{ - id: id, - algorithm: ocrypto.KeyType(alg), - legacy: a.legacyKeys[string(id)], - cryptoProvider: a.cryptoProvider, - }, nil - } - case AlgorithmRSA2048: - if _, err := a.cryptoProvider.RSAPublicKey(string(id)); err == nil { - return &KeyDetailsAdapter{ - id: id, - algorithm: ocrypto.KeyType(alg), - legacy: a.legacyKeys[string(id)], - cryptoProvider: a.cryptoProvider, - }, nil - } - } + key, ok := a.cryptoProvider.keysByID[string(id)] + if !ok { + return nil, ErrCertNotFound + } + + switch key := key.(type) { + case StandardECCrypto: + return &KeyDetailsAdapter{ + id: id, + algorithm: ocrypto.KeyType(key.Algorithm), + legacy: a.legacyKeys[string(id)], + cryptoProvider: a.cryptoProvider, + }, nil + case PrivateKeyCrypto: + return &KeyDetailsAdapter{ + id: id, + algorithm: ocrypto.KeyType(key.Algorithm), + legacy: a.legacyKeys[string(id)], + cryptoProvider: a.cryptoProvider, + }, nil + default: + return nil, ErrCertNotFound } - return nil, ErrCertNotFound } // ListKeys lists all available keys @@ -203,32 +199,36 @@ func (a *InProcessProvider) ListKeys(ctx context.Context) ([]trust.KeyDetails, e } func (a *InProcessProvider) ListKeysWith(ctx context.Context, opts trust.ListKeyOptions) ([]trust.KeyDetails, error) { - // This is a limited implementation as CryptoProvider doesn't expose a list of all keys var keys []trust.KeyDetails - // Try to find keys for known algorithms - for _, alg := range []string{AlgorithmRSA2048, AlgorithmECP256R1} { - if kids, err := a.cryptoProvider.ListKIDsByAlgorithm(alg); err == nil && len(kids) > 0 { - for _, kid := range kids { - if opts.LegacyOnly && !a.legacyKeys[kid] { - continue // Skip non-legacy keys if LegacyOnly is true - } - keys = append(keys, &KeyDetailsAdapter{ - id: trust.KeyIdentifier(kid), - algorithm: ocrypto.KeyType(alg), - cryptoProvider: a.cryptoProvider, - legacy: a.legacyKeys[kid], - }) - } - } else if err != nil { + for kid, key := range a.cryptoProvider.keysByID { + if opts.LegacyOnly && !a.legacyKeys[kid] { + continue + } + + var algorithm ocrypto.KeyType + switch key := key.(type) { + case StandardECCrypto: + algorithm = ocrypto.KeyType(key.Algorithm) + case PrivateKeyCrypto: + algorithm = ocrypto.KeyType(key.Algorithm) + default: if a.logger != nil { a.logger.WarnContext(ctx, - "failed to list keys by algorithm", - slog.String("algorithm", alg), - slog.Any("error", err), + "failed to list key with unsupported type", + slog.String("kid", kid), + slog.Any("type", fmt.Sprintf("%T", key)), ) } + continue } + + keys = append(keys, &KeyDetailsAdapter{ + id: trust.KeyIdentifier(kid), + algorithm: algorithm, + cryptoProvider: a.cryptoProvider, + legacy: a.legacyKeys[kid], + }) } return keys, nil @@ -237,30 +237,25 @@ func (a *InProcessProvider) ListKeysWith(ctx context.Context, opts trust.ListKey // Decrypt implements the unified decryption method for both RSA and EC func (a *InProcessProvider) Decrypt(ctx context.Context, keyDetails trust.KeyDetails, ciphertext []byte, ephemeralPublicKey []byte) (ocrypto.ProtectedKey, error) { kid := string(keyDetails.ID()) - - var protectedKey ocrypto.ProtectedKey - var err error - - // Try to determine the key type - keyType, err := a.determineKeyType(ctx, kid) - if err != nil { - return nil, err + key, ok := a.cryptoProvider.keysByID[kid] + if !ok { + return nil, ErrCertNotFound } var rawKey []byte - switch keyType { - case AlgorithmRSA2048: - if len(ephemeralPublicKey) > 0 { - return nil, errors.New("ephemeral public key should not be provided for RSA decryption") - } - rawKey, err = a.cryptoProvider.RSADecrypt(crypto.SHA1, kid, "", ciphertext) - - case AlgorithmECP256R1: + var err error + switch key := key.(type) { + case StandardECCrypto: if len(ephemeralPublicKey) == 0 { return nil, errors.New("ephemeral public key is required for EC decryption") } - protectedKey, err = a.cryptoProvider.ECDecrypt(ctx, kid, ephemeralPublicKey, ciphertext) - + protectedKey, err := a.cryptoProvider.ECDecrypt(ctx, kid, ephemeralPublicKey, ciphertext) + if err != nil { + return nil, err + } + return protectedKey, nil + case PrivateKeyCrypto: + rawKey, err = key.dec.DecryptWithEphemeralKey(ciphertext, ephemeralPublicKey) default: return nil, errors.New("unsupported key algorithm") } @@ -268,15 +263,7 @@ func (a *InProcessProvider) Decrypt(ctx context.Context, keyDetails trust.KeyDet if err != nil { return nil, err } - - if protectedKey == nil { - protectedKey, err = ocrypto.NewAESProtectedKey(rawKey) - if err != nil { - return nil, fmt.Errorf("failed to create protected key: %w", err) - } - } - - return protectedKey, nil + return ocrypto.NewAESProtectedKey(rawKey) } // DeriveKey computes an ECDH shared secret and derives an AES key via HKDF. @@ -334,19 +321,3 @@ func (a *InProcessProvider) GenerateECSessionKey(_ context.Context, ephemeralPub func (a *InProcessProvider) Close() { a.cryptoProvider.Close() } - -// determineKeyType tries to determine the algorithm of a key based on its ID -// This is a helper method for the Decrypt method -func (a *InProcessProvider) determineKeyType(_ context.Context, kid string) (string, error) { - // First try RSA - if _, err := a.cryptoProvider.RSAPublicKey(kid); err == nil { - return AlgorithmRSA2048, nil - } - - // Then try EC - if _, err := a.cryptoProvider.ECPublicKey(kid); err == nil { - return AlgorithmECP256R1, nil - } - - return "", errors.New("could not determine key type") -} diff --git a/service/internal/security/standard_crypto.go b/service/internal/security/standard_crypto.go index 6b86abdcbe..c80df2f5f7 100644 --- a/service/internal/security/standard_crypto.go +++ b/service/internal/security/standard_crypto.go @@ -52,10 +52,10 @@ type StandardKeyInfo struct { PublicKeyPath string `mapstructure:"public_key_path" json:"public_key_path"` } -type StandardRSACrypto struct { +type PrivateKeyCrypto struct { KeyPairInfo - asymDecryption ocrypto.AsymDecryption - asymEncryption ocrypto.AsymEncryption + dec ocrypto.PrivateKeyDecryptor + enc ocrypto.PublicKeyEncryptor } type StandardECCrypto struct { @@ -150,23 +150,19 @@ func loadKey(k KeyPairInfo) (any, error) { ecPrivateKeyPem: string(privatePEM), ecCertificatePEM: string(certPEM), }, nil - case AlgorithmRSA2048, AlgorithmRSA4096: - asymDecryption, err := ocrypto.NewAsymDecryption(string(privatePEM)) + case AlgorithmRSA2048, AlgorithmRSA4096, AlgorithmMLKEM768, AlgorithmMLKEM1024, AlgorithmXWing: + decrypter, err := ocrypto.FromPrivatePEM(string(privatePEM)) if err != nil { - return nil, fmt.Errorf("ocrypto.NewAsymDecryption failed: %w", err) + return nil, fmt.Errorf("ocrypto.FromPrivatePEM failed: %w", err) } publicKeyEncryptor, err := ocrypto.FromPublicPEM(string(certPEM)) if err != nil { return nil, fmt.Errorf("ocrypto.FromPublicPEM failed: %w", err) } - asymEncryption, ok := publicKeyEncryptor.(*ocrypto.AsymEncryption) - if !ok { - return nil, fmt.Errorf("unexpected public key encryptor type: %T", publicKeyEncryptor) - } - return StandardRSACrypto{ - KeyPairInfo: k, - asymDecryption: asymDecryption, - asymEncryption: *asymEncryption, + return PrivateKeyCrypto{ + KeyPairInfo: k, + dec: decrypter, + enc: publicKeyEncryptor, }, nil default: return nil, errors.New("unsupported algorithm [" + k.Algorithm + "]") @@ -199,25 +195,20 @@ func loadDeprecatedKeys(rsaKeys map[string]StandardKeyInfo, ecKeys map[string]St if err != nil { return nil, fmt.Errorf("failed to rsa public key file: %w", err) } - publicKeyEncryptor, err := ocrypto.FromPublicPEM(string(publicPemData)) if err != nil { return nil, fmt.Errorf("ocrypto.FromPublicPEM failed: %w", err) } - asymEncryption, ok := publicKeyEncryptor.(*ocrypto.AsymEncryption) - if !ok { - return nil, fmt.Errorf("unexpected public key encryptor type: %T", publicKeyEncryptor) - } - k := StandardRSACrypto{ + k := PrivateKeyCrypto{ KeyPairInfo: KeyPairInfo{ Algorithm: AlgorithmRSA2048, KID: id, Private: kasInfo.PrivateKeyPath, Certificate: kasInfo.PublicKeyPath, }, - asymDecryption: asymDecryption, - asymEncryption: *asymEncryption, + dec: asymDecryption, + enc: publicKeyEncryptor, } keysByAlg[AlgorithmRSA2048][id] = k keysByID[id] = k @@ -266,22 +257,26 @@ func (s StandardCrypto) FindKID(alg string) string { return "" } -func (s StandardCrypto) RSAPublicKey(kid string) (string, error) { +func (s StandardCrypto) PublicKey(kid string) (string, error) { k, ok := s.keysByID[kid] if !ok { - return "", fmt.Errorf("no rsa key with id [%s]: %w", kid, ErrCertNotFound) - } - rsa, ok := k.(StandardRSACrypto) - if !ok { - return "", fmt.Errorf("key with id [%s] is not an RSA key: %w", kid, ErrCertNotFound) + return "", fmt.Errorf("no public key with id [%s]: %w", kid, ErrCertNotFound) } - - pem, err := rsa.asymEncryption.PublicKeyInPemFormat() - if err != nil { - return "", fmt.Errorf("failed to retrieve rsa public key file: %w", err) + switch key := k.(type) { + case PrivateKeyCrypto: + publicKeyPEM, err := key.enc.PublicKeyInPemFormat() + if err != nil { + return "", fmt.Errorf("failed to retrieve public key file: %w", err) + } + return publicKeyPEM, nil + case StandardECCrypto: + if key.ecCertificatePEM != "" { + return key.ecCertificatePEM, nil + } + return s.ECPublicKey(kid) + default: + return "", fmt.Errorf("key with id [%s] does not expose a public key: %w", kid, ErrCertNotFound) } - - return pem, nil } func (s StandardCrypto) ECCertificate(kid string) (string, error) { @@ -334,12 +329,12 @@ func (s StandardCrypto) RSADecrypt(_ crypto.Hash, kid string, _ string, cipherte if !ok { return nil, ErrCertNotFound } - rsa, ok := k.(StandardRSACrypto) + rsa, ok := k.(PrivateKeyCrypto) if !ok { return nil, ErrCertNotFound } - data, err := rsa.asymDecryption.Decrypt(ciphertext) + data, err := rsa.dec.Decrypt(ciphertext) if err != nil { return nil, fmt.Errorf("error decrypting data: %w", err) } @@ -355,12 +350,17 @@ func (s StandardCrypto) RSAPublicKeyAsJSON(kid string) (string, error) { if !ok { return "", ErrCertNotFound } - rsa, ok := k.(StandardRSACrypto) + rsa, ok := k.(PrivateKeyCrypto) if !ok { return "", ErrCertNotFound } - rsaPublicKeyJwk, err := jwk.FromRaw(rsa.asymEncryption.PublicKey) + asymEncryption, ok := rsa.enc.(*ocrypto.AsymEncryption) + if !ok { + return "", fmt.Errorf("key with id [%s] does not support JWK export: %w", kid, ErrCertNotFound) + } + + rsaPublicKeyJwk, err := jwk.FromRaw(asymEncryption.PublicKey) if err != nil { return "", fmt.Errorf("jwk.FromRaw: %w", err) } @@ -429,12 +429,8 @@ func (s *StandardCrypto) Decrypt(_ context.Context, keyID trust.KeyIdentifier, c return nil, fmt.Errorf("failed to decrypt with ephemeral key: %w", err) } - case StandardRSACrypto: - if len(ephemeralPublicKey) > 0 { - return nil, errors.New("ephemeral public key should not be provided for RSA decryption") - } - - rawKey, err = key.asymDecryption.Decrypt(ciphertext) + case PrivateKeyCrypto: + rawKey, err = key.dec.DecryptWithEphemeralKey(ciphertext, ephemeralPublicKey) if err != nil { return nil, fmt.Errorf("error decrypting data: %w", err) } diff --git a/service/kas/access/publicKey.go b/service/kas/access/publicKey.go index eb5b1fe780..08b8d66818 100644 --- a/service/kas/access/publicKey.go +++ b/service/kas/access/publicKey.go @@ -149,11 +149,11 @@ func (p *Provider) PublicKey(ctx context.Context, req *connect.Request[kaspb.Pub } switch algorithm { - case security.AlgorithmECP256R1: + case security.AlgorithmECP256R1, security.AlgorithmECP384R1, security.AlgorithmECP521R1: // For EC keys, export the public key ecPublicKeyPem, err := keyDetails.ExportPublicKey(ctx, trust.KeyTypePKCS8) return r(ecPublicKeyPem, kid, err) - case security.AlgorithmRSA2048: + case security.AlgorithmMLKEM768, security.AlgorithmMLKEM1024, security.AlgorithmXWing, security.AlgorithmRSA2048, security.AlgorithmRSA4096: fallthrough case "": switch fmt { diff --git a/service/kas/access/rewrap.go b/service/kas/access/rewrap.go index d62975a480..78ebe0a62e 100644 --- a/service/kas/access/rewrap.go +++ b/service/kas/access/rewrap.go @@ -667,6 +667,28 @@ func (p *Provider) verifyRewrapRequests(ctx context.Context, req *kaspb.Unsigned var dek ocrypto.ProtectedKey var err error switch kao.GetKeyAccessObject().GetKeyType() { + case string(ocrypto.MLKEM), string(ocrypto.Hybrid): + ephemeralCiphertext := kao.GetKeyAccessObject().GetEphemeralPublicKey() + if ephemeralCiphertext == "" { + p.Logger.WarnContext(ctx, "missing encapsulated key for kem rewrap") + failedKAORewrap(results, kao, err400("bad request")) + continue + } + + encapsulatedKey, err := ocrypto.Base64Decode([]byte(ephemeralCiphertext)) + if err != nil { + p.Logger.WarnContext(ctx, "failed to decode encapsulated key for kem rewrap", slog.Any("error", err)) + failedKAORewrap(results, kao, err400("bad request")) + continue + } + + kid := trust.KeyIdentifier(kao.GetKeyAccessObject().GetKid()) + dek, err = p.KeyDelegator.Decrypt(ctx, kid, kao.GetKeyAccessObject().GetWrappedKey(), encapsulatedKey) + if err != nil { + p.Logger.WarnContext(ctx, "failed to decrypt KEM key", slog.Any("error", err)) + failedKAORewrap(results, kao, err400("bad request")) + continue + } case "ec-wrapped": if !p.ECTDFEnabled && !p.Preview.ECTDFEnabled { diff --git a/service/kas/key_indexer.go b/service/kas/key_indexer.go index 021b3f418a..20d24a91c0 100644 --- a/service/kas/key_indexer.go +++ b/service/kas/key_indexer.go @@ -58,6 +58,12 @@ func convertEnumToAlg(alg policy.Algorithm) ocrypto.KeyType { return ocrypto.EC384Key case policy.Algorithm_ALGORITHM_EC_P521: return ocrypto.EC521Key + case policy.Algorithm_ALGORITHM_MLKEM_768: + return ocrypto.MLKEM768Key + case policy.Algorithm_ALGORITHM_MLKEM_1024: + return ocrypto.MLKEM1024Key + case policy.Algorithm_ALGORITHM_XWING: + return ocrypto.HybridXWing case policy.Algorithm_ALGORITHM_UNSPECIFIED: fallthrough default: @@ -77,6 +83,12 @@ func convertAlgToEnum(alg string) (policy.Algorithm, error) { return policy.Algorithm_ALGORITHM_EC_P384, nil case string(ocrypto.EC521Key): return policy.Algorithm_ALGORITHM_EC_P521, nil + case string(ocrypto.MLKEM768Key): + return policy.Algorithm_ALGORITHM_MLKEM_768, nil + case string(ocrypto.MLKEM1024Key): + return policy.Algorithm_ALGORITHM_MLKEM_1024, nil + case string(ocrypto.HybridXWing): + return policy.Algorithm_ALGORITHM_XWING, nil default: return policy.Algorithm_ALGORITHM_UNSPECIFIED, fmt.Errorf("unsupported algorithm: %s", alg) } diff --git a/service/pkg/db/marshalHelpers.go b/service/pkg/db/marshalHelpers.go index 667ab27e21..1953fc8815 100644 --- a/service/pkg/db/marshalHelpers.go +++ b/service/pkg/db/marshalHelpers.go @@ -157,6 +157,12 @@ func FormatAlg(alg policy.Algorithm) (string, error) { return "ec:secp384r1", nil case policy.Algorithm_ALGORITHM_EC_P521: return "ec:secp521r1", nil + case policy.Algorithm_ALGORITHM_MLKEM_768: + return "mlkem:768", nil + case policy.Algorithm_ALGORITHM_MLKEM_1024: + return "mlkem:1024", nil + case policy.Algorithm_ALGORITHM_XWING: + return "hpqt:xwing", nil case policy.Algorithm_ALGORITHM_UNSPECIFIED: fallthrough default: diff --git a/service/policy/db/grant_mappings.go b/service/policy/db/grant_mappings.go index 7c3688af58..7e68e11aa7 100644 --- a/service/policy/db/grant_mappings.go +++ b/service/policy/db/grant_mappings.go @@ -22,6 +22,12 @@ func mapAlgorithmToKasPublicKeyAlg(alg policy.Algorithm) policy.KasPublicKeyAlgE return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 case policy.Algorithm_ALGORITHM_EC_P521: // ALGORITHM_EC_P521 is an alias return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 + case policy.Algorithm_ALGORITHM_MLKEM_768: + return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 + case policy.Algorithm_ALGORITHM_MLKEM_1024: + return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 + case policy.Algorithm_ALGORITHM_XWING: + return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_XWING case policy.Algorithm_ALGORITHM_UNSPECIFIED: return policy.KasPublicKeyAlgEnum_KAS_PUBLIC_KEY_ALG_ENUM_UNSPECIFIED default: diff --git a/service/policy/kasregistry/key_access_server_registry.proto b/service/policy/kasregistry/key_access_server_registry.proto index f7c4bdf7f8..ae65fa19f9 100644 --- a/service/policy/kasregistry/key_access_server_registry.proto +++ b/service/policy/kasregistry/key_access_server_registry.proto @@ -403,7 +403,7 @@ message CreateKeyRequest { Algorithm key_algorithm = 3 [(buf.validate.field).cel = { id: "key_algorithm_defined" message: "The key_algorithm must be one of the defined values." - expression: "this in [1, 2, 3, 4, 5]" // Allow ALGORITHM_RSA_2048, ALGORITHM_RSA_4096, ALGORITHM_EC_P256, ALGORITHM_EC_P384, ALGORITHM_EC_P521 + expression: "this in [1, 2, 3, 4, 5, 20, 21]" // Allow RSA, EC, and ML-KEM algorithms }]; // The algorithm to be used for the key // Required KeyMode key_mode = 4 [(buf.validate.field).cel = { @@ -550,7 +550,7 @@ message RotateKeyRequest { Algorithm algorithm = 2 [(buf.validate.field).cel = { id: "key_algorithm_defined" message: "The key_algorithm must be one of the defined values." - expression: "this in [1, 2, 3, 4, 5]" // Allow ALGORITHM_RSA_2048, ALGORITHM_RSA_4096, ALGORITHM_EC_P256, ALGORITHM_EC_P384, ALGORITHM_EC_P521 + expression: "this in [1, 2, 3, 4, 5, 20, 21]" // Allow RSA, EC, and ML-KEM algorithms }]; // Required KeyMode key_mode = 3 [ diff --git a/service/policy/objects.proto b/service/policy/objects.proto index a54bc2517a..d4ccd939ad 100644 --- a/service/policy/objects.proto +++ b/service/policy/objects.proto @@ -388,6 +388,9 @@ enum KasPublicKeyAlgEnum { KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP256R1 = 5; KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP384R1 = 6; KAS_PUBLIC_KEY_ALG_ENUM_EC_SECP521R1 = 7; + KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_768 = 20; + KAS_PUBLIC_KEY_ALG_ENUM_MLKEM_1024 = 21; + KAS_PUBLIC_KEY_ALG_ENUM_XWING = 30; } // Deprecated @@ -553,6 +556,9 @@ enum Algorithm { ALGORITHM_EC_P256 = 3; ALGORITHM_EC_P384 = 4; ALGORITHM_EC_P521 = 5; + ALGORITHM_MLKEM_768 = 20; + ALGORITHM_MLKEM_1024 = 21; + ALGORITHM_XWING = 30; } // The status of the key