06-signing-component-versions

command
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 17, 2024 License: Apache-2.0 Imports: 27 Imported by: 0

README

Signing Component Versions

This tour illustrates the basic functionality to sign and verify signatures.

It covers two basic scenarios:

  • sign Create, Sign, Transport and Verify a component version.
  • context Using context settings to configure signing and verification in target repo.

Running the examples

You can call the main program with a config file option (--config <file>) and the name of the scenario. The config file should have the following content:

targetRepository:
  type: CommonTransportFormat
  filePath: /tmp/example06.target.ctf
  fileFormat: directory
  accessMode: 2
ocmConfig: <your ocm config file>

The actual version of the example just works with the file system target, because it is not possible to specify credentials for the target repository in this simple config file. But, if you specific an OCM config file you can add more credential settings to make target repositories possible requiring credentials.

Walkthrough

Create, Sign, Transport and Verify a component version

As usual, we start with getting access to an OCM context

	ctx := ocm.DefaultContext()

Then, we configure this context with optional ocm config defined in our config file. See OCM config scenario in tour 04.

	err := ReadConfiguration(ctx, cfg)
	if err != nil {
		return err
	}

To sign a component version we need a private key. For this example, we just create a local keypair. To be able to verify later, we should save the public key, but here we do all this in a single program.

	privkey, pubkey, err := rsa.CreateKeyPair()
	if err != nil {
		return errors.Wrapf(err, "cannot create keypair")
	}

And we need a component version to sign. We again compose a component version without a repository (see tour02 example 2).

	cv := composition.NewComponentVersion(ctx, "acme.org/example6", "v0.1.0")

	// just use the same component version setup again
	err = setupVersion(cv)
	if err != nil {
		return errors.Wrapf(err, "version composition")
	}

	fmt.Printf("*** composition version ***\n")
	err = describeVersion(cv)

Now, let's sign the component version. There might be multiple signatures, therefore every signature has a name (here acme.org). Keys are always specified for a dedicated signature name. The signing process can be influenced by several options. Here, we just provide the private key to be used in an ad-hoc manner. Later, we will see how everything can be preconfigured in a signing context.

	_, err = signing.SignComponentVersion(cv, "acme.org", signing.PrivateKey("acme.org", privkey))
	if err != nil {
		return errors.Wrapf(err, "cannot sign component version")
	}
	fmt.Printf("*** signed composition version ***\n")
	err = describeVersion(cv)

Now, we add the signed component version to a target repository. Here, we just reuse the code from tour02

	fmt.Printf("target repository is %s\n", string(cfg.Target))
	target, err := ctx.RepositoryForConfig(cfg.Target, nil)
	if err != nil {
		return errors.Wrapf(err, "cannot open repository")
	}
	defer target.Close()

	err = target.AddComponentVersion(cv, true)
	if err != nil {
		return errors.Wrapf(err, "cannot store signed version")
	}

Let's check the target for the new component version.

	tcv, err := target.LookupComponentVersion("acme.org/example6", "v0.1.0")
	if err != nil {
		return errors.Wrapf(err, "transported version not found")
	}
	defer tcv.Close()

	// please be aware that the signature should be stored.
	fmt.Printf("*** target version in transportation target\n")
	err = describeVersion(tcv)
	if err != nil {
		return errors.Wrapf(err, "describe failed")
	}

Please note, that the version now contains a signature.

Finally, we check whether the signature is still valid for the target version.

	_, err = signing.VerifyComponentVersion(cv, "acme.org", signing.PublicKey("acme.org", pubkey))
	if err != nil {
		return errors.Wrapf(err, "verification failed")
	} else {
		fmt.Printf("verification succeeded\n")
	}
Using Context Settings to Configure Signing

Instead of providing all signing relevant information directly with the signing or verification calls, it is possible to preconfigure various information at the OCM context.

As usual, we start with getting access to an OCM context

	ctx := ocm.DefaultContext()

Then, we configure this context with optional ocm config defined in our config file. See OCM config scenario in tour 04.

	err := ReadConfiguration(ctx, cfg)
	if err != nil {
		return err
	}

To sign a component version we need a private key. For this example, we again just create a local keypair. To be able to verify later, we should save the public key, but here we do all this in a single program.

	privkey, pubkey, err := rsa.CreateKeyPair()
	if err != nil {
		return errors.Wrapf(err, "cannot create keypair")
	}

Finally, we create a component version in our target repository. The called function

	err = prepareComponentInRepo(ctx, cfg)
	if err != nil {
		return errors.Wrapf(err, "cannot prepare component version in target repo")
	}

executes the same coding already shown in the previous example.

Signing Using Manual Context Settings

After this preparation we now configure the signing part of the OCM context. Every OCM context features a signing registry, which provides available signers and hashers, but also keys and certificates for various purposes. It is always asked if a key is required, which is not explicitly given to a signing/verification call.

This context part is implemented as additional attribute stored along with the context. Attributes are always implemented as a separate package containing the attribute structure, its deserialization and a Get(Context) function to retrieve the attribute for the context. This way new arbitrary attributes for various use cases can be added without the need to change the context interface.

	siginfo := signingattr.Get(ctx)

Now, we manually add the keys to our context.

	siginfo.RegisterPrivateKey("acme.org", privkey)
	siginfo.RegisterPublicKey("acme.org", pubkey)

We are prepared now and can sign any component version without specifying further options in any repository for the signature name acme.org.

Therefore, we just get the component version from the prepared repository

	fmt.Printf("repository is %s\n", string(cfg.Target))
	repo, err := ctx.RepositoryForConfig(cfg.Target, nil)
	if err != nil {
		return errors.Wrapf(err, "cannot open repository")
	}
	defer repo.Close()

	cv, err := repo.LookupComponentVersion("acme.org/example6", "v0.1.0")
	if err != nil {
		return errors.Wrapf(err, "version not found")
	}
	defer cv.Close()

and finally sign it. We don't need to present the key, here. It is taken from the context.

	_, err = signing.SignComponentVersion(cv, "acme.org")
	if err != nil {
		return errors.Wrapf(err, "cannot sign component version")
	}

The same way we can just call VerifyComponentVersion to verify the signature.

	_, err = signing.VerifyComponentVersion(cv, "acme.org")
	if err != nil {
		return errors.Wrapf(err, "verification failed")
	} else {
		fmt.Printf("verification succeeded\n")
	}
Configuring Keys with OCM Configuration File

Manually adding keys to the signing attribute might simplify the call to possibly multiple signing/verification calls, but it does not help to provide keys via an external configuration (for example for using the OCM CLI). In tour04 we have seen how arbitrary configuration possibilities can be added. The signing attribute uses this mechanism to configure itself by providing an own configuration object, which can be used to feed keys (and certificates) into the signing attribute of an OCM context.

	sigcfg := signingattr.New()

It provides methods to add elements like keys and certificates, which convert these elements into a (de-)serializable form.

	sigcfg.AddPrivateKey("acme.org", privkey)
	sigcfg.AddPublicKey("acme.org", pubkey)

	ocmcfg := configcfg.New()
	ocmcfg.AddConfig(sigcfg)

By adding this config to a generic configuration object you get an OCM config usable to predefine keys for your CLI.

	data, err := runtime.DefaultYAMLEncoding.Marshal(ocmcfg)
	if err != nil {
		return err
	}
	fmt.Printf("ocm config file configuring standard keys:\n--- begin ocmconfig ---\n%s--- end ocmconfig ---\n", string(data))

And here is a sample output containing the public and private key.

configurations:
- privateKeys:
    acme.org:
      stringdata: |
        -----BEGIN RSA PRIVATE KEY-----
        MIIEowIBAAKCAQEAx0v6JLoV1mrYpX1H86G5bMVBF2ftyxy/IJh1WJMv6xp6q3aK
        y+WQEJu/O6Lnh9XdZWRpz36dTJ9AnraEF/kNktJW9zMQQjWpXqjM3nwL5Eczc1ek
        jkK6Xa5NZ+HC2lhgPXAsYhkF93EDPd4lKn9g+E68KKPrBy/FDdFnu1YL3tSERF65
        FIJKLftKEPTJEjlHtG0bxdLWvEaTgLg4yxiw8ZMBBO1A1nyqf5zdQkxAEbpNuSGh
        tinvqZ1CNhhbnechWxFp3XgDV+rKT5s1PWdgNgM4b26v4ocH+4b3iugi53WDcdD1
        1TrIeh3Tv5gCVyd3qdrJVOr4/JH4Ut8Pmrx3SwIDAQABAoIBAAJH3k75SjKv+la6
        fk5NdX/HKh2IdPI0HAPVetJOrOe1392Cd8gpkmJ+Rcv660dkrSnx4jwbqNUtpWGr
        mQtlMECT46bkkLURRCTvLZkGNmSgY/hX8mwmW1ejHQOpU7+H72IEnJ1qx+SzCGR2
        3FoGJyfwTrrwVUo5w7mKwfMk4vHj24muIsc8Jcvy7zQsuDHpS+QKTCppJjSO68ZQ
        XYxonlb+a9alQdoN+CDJ4ic5N7YTs1ofTSnkqBmIT6fW8CcYpJWBh2Km/shzh1Ll
        9mqda0p9HJRmBZ9q1+Jy5W5g7P3O0rKoS1XAXqFnbG0Ux9AMX9GG8xEo7fhG+UuQ
        IpEke5ECgYEA8BtlcYPkmmsZzeEmO/hvLZibfO+PShHOnb2XyA6+jM+3rlAaFdDv
        qVFhS3cuGFpQX1csvPamQ0+ZhoOCAMiyZ2UkurMR6HaBlCL5akn53LiIOX+rsrUV
        f4cGpFSnraMV6sc2D2hK68id9plZheDoGVlI55MtvnfIv/N0M26JWSUCgYEA1H0N
        mmHg6x7KkEnGO6Xo47jElFrvXwbtQ9jsmsv/F0s9V821pMVw8/OE+d0U9h9hQFz0
        dGIM5EQHbHTx1bN71TdsCeYAlJWHU2Ifoyi7XqhZ3PhuVMAOyN4MUAvN/C9GKkBz
        /Z7fWQQVbQUQcUGeTfbg3k5eoo4FcNnPKu0JO68CgYEAzEmC9iIRzpBxVAnMThoB
        /flp0dLBR3P/J5a4HS7uUUAqN9VPXGB4iMcE0QCF55Jv765sEhqJO5vuM9SQN7qK
        kA4uQes5wV+SwEdBjn2CaZlXzhQiMdqAgBCSRh8Ay2uGqkr0ZAeINzRpsfanhJDm
        6SpeLSm8MeIYm7i3lUrm8UECgYAe58FevvWzvNrBebl3W34wAOO2oDNIov1HbPmc
        2ibUAIF/j8/nk0AGe1jP7rPpyE6gyeRUOR6e5LYftDKoXl6YeGMiXW2gLs9r9U2c
        sYPvFJVdalTBxt1focwwqEbhcw7FfnJgZQcfL1TecmodzulzdYDnVIa3JejsrQFQ
        wQEiyQKBgDIIT2zJJtidLfs2m1wffjN+DqnhDvG/0rD9ATNYVdAdMijijsBptx4B
        aY+Q1cwPcpS/YhF+NQv/CahUu+0gLGfiVjf+bkRlfvCyPYZgoSPsm3IBIHahPAoE
        5G+GqidX8uul4/ZtUXzZl/kbygCB9PGRaf93rXcyW2OUxF2kCOxF
        -----END RSA PRIVATE KEY-----
  publicKeys:
    acme.org:
      stringdata: |
        -----BEGIN RSA PUBLIC KEY-----
        MIIBCgKCAQEAx0v6JLoV1mrYpX1H86G5bMVBF2ftyxy/IJh1WJMv6xp6q3aKy+WQ
        EJu/O6Lnh9XdZWRpz36dTJ9AnraEF/kNktJW9zMQQjWpXqjM3nwL5Eczc1ekjkK6
        Xa5NZ+HC2lhgPXAsYhkF93EDPd4lKn9g+E68KKPrBy/FDdFnu1YL3tSERF65FIJK
        LftKEPTJEjlHtG0bxdLWvEaTgLg4yxiw8ZMBBO1A1nyqf5zdQkxAEbpNuSGhtinv
        qZ1CNhhbnechWxFp3XgDV+rKT5s1PWdgNgM4b26v4ocH+4b3iugi53WDcdD11TrI
        eh3Tv5gCVyd3qdrJVOr4/JH4Ut8Pmrx3SwIDAQAB
        -----END RSA PUBLIC KEY-----
  type: keys.config.ocm.software
type: generic.config.ocm.software

Documentation

The Go Gopher

There is no documentation for this package.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL