Browse Source

Support serializing encrypted secret objects

In memory, we store secrets as deserialized YAML objects, which
can be dicts, lists, scalars, or our own custom EncryptedPKCS1_OAEP
subclass of YamlObject.  We need to be able to serialize a
(potentially) encrypted secret in order to transmit it to the
executor via ZK.  The easiest and most sensible serialization
mechanism is to reverse our deserialization and use YAML.

This change supports this by indicating that an EncryptedPKCS1_OAEP
object is safe to serialize using the SafeDumper.  Any time we
safe_dump to yaml a Python data structure with an EncryptedPKCS1_OAEP
in it, it will be serialized using our custom tag.

This also corrects an unrelated parameter name typo in the
safe_dump method.

Change-Id: I1034946e5261003d9e83119ceee99b89d0dc53a1
James E. Blair 2 months ago
2 changed files with 22 additions and 3 deletions
  1. +11
  2. +11

+ 11
- 0
zuul/ View File

@ -412,6 +412,7 @@ repo {repo} on branch {branch}. The error was:
class EncryptedPKCS1_OAEP(yaml.YAMLObject):
yaml_tag = u'!encrypted/pkcs1-oaep'
yaml_loader = yaml.SafeLoader
yaml_dumper = yaml.SafeDumper
def __init__(self, ciphertext):
if isinstance(ciphertext, list):
@ -432,6 +433,16 @@ class EncryptedPKCS1_OAEP(yaml.YAMLObject):
def from_yaml(cls, loader, node):
return cls(node.value)
def to_yaml(cls, dumper, data):
ciphertext = data.ciphertext
if isinstance(ciphertext, list):
ciphertext = [yaml.ScalarNode(value=base64.b64encode(x))
for x in ciphertext]
ciphertext = base64.b64encode(ciphertext)
return yaml.ScalarNode(tag=cls.yaml_tag, value=ciphertext)
def decrypt(self, private_key):
if isinstance(self.ciphertext, list):
return ''.join([

+ 11
- 3
zuul/lib/ View File

@ -9,8 +9,11 @@
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import types
import yaml
from yaml import YAMLObject, YAMLError # noqa: F401
from yaml import ( # noqa: F401
YAMLObject, YAMLError, ScalarNode, MappingNode
# Explicit type ignore to deal with provisional import failure
@ -26,9 +29,14 @@ except ImportError:
Mark = yaml.Mark
def safe_load(stream, *args, **kwargs):
return yaml.load(stream, *args, Loader=SafeLoader, **kwargs)
def safe_dump(stream, *args, **kwargs):
return yaml.dump(stream, *args, Dumper=SafeDumper, **kwargs)
def safe_dump(data, *args, **kwargs):
return yaml.dump(data, *args, Dumper=SafeDumper, **kwargs)