kyber: go.dedis.ch/kyber/examples Index | Examples | Files

package examples

import "go.dedis.ch/kyber/examples"

Package examples provides a suite of tests showing how to use the different abstraction and protocols provided by the kyber library. To run the tests, simply do `go test -v` in this directory.

This example illustrates how to use the crypto toolkit's kyber.group API to perform basic Diffie-Hellman key exchange calculations, using the NIST-standard P256 elliptic curve in this case. Any other suitable elliptic curve or other cryptographic group may be used simply by changing the first line that picks the suite.

Code:

// A pseudo RNG which makes this code repeatable for testing.
rng := blake2xb.New(nil)

// Crypto setup: NIST-standardized P256 curve with AES-128 and SHA-256
// For production code, simply use edwards25519.NewBlakeSHA256Ed25519().
suite := edwards25519.NewBlakeSHA256Ed25519WithRand(rng)

// Alice's public/private keypair
a := suite.Scalar().Pick(rng)  // Alice's private key
A := suite.Point().Mul(a, nil) // Alice's public key

// Bob's public/private keypair
b := suite.Scalar().Pick(rng)  // Alice's private key
B := suite.Point().Mul(b, nil) // Alice's public key

// Assume Alice and Bob have securely obtained each other's public keys.

// Alice computes their shared secret using Bob's public key.
SA := suite.Point().Mul(a, B)

// Bob computes their shared secret using Alice's public key.
SB := suite.Point().Mul(b, A)

// They had better be the same!
if !SA.Equal(SB) {
    panic("Diffie-Hellman key exchange didn't work")
}
fmt.Println("Shared secret: " + SA.String())

Output:

Shared secret: 80ea238cacfdab279626970bba18c69083c7751865dec4c6434bff4351282847

This example illustrates how the crypto toolkit may be used to perform "pure" ElGamal encryption, in which the message to be encrypted is small enough to be embedded directly within a group element (e.g., in an elliptic curve point). For basic background on ElGamal encryption see for example http://en.wikipedia.org/wiki/ElGamal_encryption.

Most public-key crypto libraries tend not to support embedding data in points, in part because for "vanilla" public-key encryption you don't need it: one would normally just generate an ephemeral Diffie-Hellman secret and use that to seed a symmetric-key crypto algorithm such as AES, which is much more efficient per bit and works for arbitrary-length messages. However, in many advanced public-key crypto algorithms it is often useful to be able to embedded data directly into points and compute with them: as just one of many examples, the proactively verifiable anonymous messaging scheme prototyped in Verdict (see http://dedis.cs.yale.edu/dissent/papers/verdict-abs).

For fancier versions of ElGamal encryption implemented in this toolkit see for example anon.Encrypt, which encrypts a message for one of several possible receivers forming an explicit anonymity set.

Code:

package main

import (
    "fmt"

    "go.dedis.ch/kyber/v4"
    "go.dedis.ch/kyber/v4/group/edwards25519"
    "go.dedis.ch/kyber/v4/util/random"
)

func ElGamalEncrypt(group kyber.Group, pubkey kyber.Point, message []byte) (
    K, C kyber.Point, remainder []byte) {

    // Embed the message (or as much of it as will fit) into a curve point.
    M := group.Point().Embed(message, random.New())
    max := group.Point().EmbedLen()
    if max > len(message) {
        max = len(message)
    }
    remainder = message[max:]
    // ElGamal-encrypt the point to produce ciphertext (K,C).
    k := group.Scalar().Pick(random.New()) // ephemeral private key
    K = group.Point().Mul(k, nil)          // ephemeral DH public key
    S := group.Point().Mul(k, pubkey)      // ephemeral DH shared secret
    C = S.Add(S, M)                        // message blinded with secret
    return
}

func ElGamalDecrypt(group kyber.Group, prikey kyber.Scalar, K, C kyber.Point) (
    message []byte, err error) {

    // ElGamal-decrypt the ciphertext (K,C) to reproduce the message.
    S := group.Point().Mul(prikey, K) // regenerate shared secret
    M := group.Point().Sub(C, S)      // use to un-blind the message
    message, err = M.Data()           // extract the embedded data
    return
}

/*
This example illustrates how the crypto toolkit may be used
to perform "pure" ElGamal encryption,
in which the message to be encrypted is small enough to be embedded
directly within a group element (e.g., in an elliptic curve point).
For basic background on ElGamal encryption see for example
http://en.wikipedia.org/wiki/ElGamal_encryption.

Most public-key crypto libraries tend not to support embedding data in points,
in part because for "vanilla" public-key encryption you don't need it:
one would normally just generate an ephemeral Diffie-Hellman secret
and use that to seed a symmetric-key crypto algorithm such as AES,
which is much more efficient per bit and works for arbitrary-length messages.
However, in many advanced public-key crypto algorithms it is often useful
to be able to embedded data directly into points and compute with them:
as just one of many examples,
the proactively verifiable anonymous messaging scheme prototyped in Verdict
(see http://dedis.cs.yale.edu/dissent/papers/verdict-abs).

For fancier versions of ElGamal encryption implemented in this toolkit
see for example anon.Encrypt, which encrypts a message for
one of several possible receivers forming an explicit anonymity set.
*/
func main() {
    suite := edwards25519.NewBlakeSHA256Ed25519()

    // Create a public/private keypair
    a := suite.Scalar().Pick(suite.RandomStream()) // Alice's private key
    A := suite.Point().Mul(a, nil)                 // Alice's public key

    // ElGamal-encrypt a message using the public key.
    m := []byte("The quick brown fox")
    K, C, _ := ElGamalEncrypt(suite, A, m)

    // Decrypt it using the corresponding private key.
    mm, err := ElGamalDecrypt(suite, a, K, C)

    // Make sure it worked!
    if err != nil {
        fmt.Println("decryption failed: " + err.Error())
    }
    if string(mm) != string(m) {
        fmt.Println("decryption produced wrong output: " + string(mm))
    }
    fmt.Println("Decryption succeeded: " + string(mm))

}

This example shows how to perform a simple Schnorr signature. Please, use this example as a reference to understand the abstraction only. There is a `sign/schnorr` package which provides Schnorr signatures functionality in a more secure manner.

Code:

package main

import (
    "bytes"
    "crypto/cipher"
    "encoding/hex"
    "errors"
    "fmt"

    "go.dedis.ch/kyber/v4"
    "go.dedis.ch/kyber/v4/group/edwards25519"
)

type Suite interface {
    kyber.Group
    kyber.Encoding
    kyber.XOFFactory
}

// A basic, verifiable signature
type basicSig struct {
    C   kyber.Scalar // challenge
    R   kyber.Scalar // response
}

// Returns a secret that depends on on a message and a point
func hashSchnorr(suite Suite, message []byte, p kyber.Point) kyber.Scalar {
    pb, _ := p.MarshalBinary()
    c := suite.XOF(pb)
    c.Write(message)
    return suite.Scalar().Pick(c)
}

// This simplified implementation of Schnorr Signatures is based on
// crypto/anon/sig.go
// The ring structure is removed and
// The anonimity set is reduced to one public key = no anonimity
func SchnorrSign(suite Suite, random cipher.Stream, message []byte,
    privateKey kyber.Scalar) []byte {

    // Create random secret v and public point commitment T
    v := suite.Scalar().Pick(random)
    T := suite.Point().Mul(v, nil)

    // Create challenge c based on message and T
    c := hashSchnorr(suite, message, T)

    // Compute response r = v - x*c
    r := suite.Scalar()
    r.Mul(privateKey, c).Sub(v, r)

    // Return verifiable signature {c, r}
    // Verifier will be able to compute v = r + x*c
    // And check that hashElgamal for T and the message == c
    buf := bytes.Buffer{}
    sig := basicSig{c, r}
    _ = suite.Write(&buf, &sig)
    return buf.Bytes()
}

func SchnorrVerify(suite Suite, message []byte, publicKey kyber.Point,
    signatureBuffer []byte) error {

    // Decode the signature
    buf := bytes.NewBuffer(signatureBuffer)
    sig := basicSig{}
    if err := suite.Read(buf, &sig); err != nil {
        return err
    }
    r := sig.R
    c := sig.C

    // Compute base**(r + x*c) == T
    var P, T kyber.Point
    P = suite.Point()
    T = suite.Point()
    T.Add(T.Mul(r, nil), P.Mul(c, publicKey))

    // Verify that the hash based on the message and T
    // matches the challange c from the signature
    c = hashSchnorr(suite, message, T)
    if !c.Equal(sig.C) {
        return errors.New("invalid signature")
    }

    return nil
}

// This example shows how to perform a simple Schnorr signature. Please, use this
// example as a reference to understand the abstraction only. There is a
// `sign/schnorr` package which provides Schnorr signatures functionality in a
// more secure manner.
func main() {
    // Crypto setup
    suite := edwards25519.NewBlakeSHA256Ed25519()
    rand := suite.XOF([]byte("example"))

    // Create a public/private keypair (X,x)
    x := suite.Scalar().Pick(rand) // create a private key x
    X := suite.Point().Mul(x, nil) // corresponding public key X

    // Generate the signature
    M := []byte("Hello World!") // message we want to sign
    sig := SchnorrSign(suite, rand, M, x)
    fmt.Print("Signature:\n" + hex.Dump(sig))

    // Verify the signature against the correct message
    err := SchnorrVerify(suite, M, X, sig)
    if err != nil {
        panic(err.Error())
    }
    fmt.Println("Signature verified against correct message.")

}

Index

Examples

Package Files

main.go

Updated 2019-10-16. Refresh now. Tools for package owners.