Documentation
¶
Overview ¶
Package keyed holds the part of BRC-369 keyed content that every application keyed under a random scalar shares: the content key, its symmetric key and its commitment (BRC-369 sections 2.1 and 2.2), the check a holder runs on a key it has just unwrapped, and BRC-2's symmetric form, in which a key is wrapped and a certificate field is encrypted.
A content key is a random scalar, never derived from an identity or from anything else. The commitment is published; the key is released only to those meant to hold it. A holder that unwraps a key checks it against the commitment before anything uses it (CheckOpened), so that a wrong key names whoever wrapped it and not whoever encrypted under it.
What a key encrypts, who it is released to and how a release is framed are the application's. This package writes no record and names no protocol.
Index ¶
- Constants
- Variables
- func CheckOpened(plaintext []byte, commitment [32]byte) ([32]byte, error)
- func CheckScalar(k [32]byte) error
- func Commitment(k [32]byte) [32]byte
- func SampleKey(r io.Reader) ([32]byte, error)
- func SymmetricKey(k [32]byte) [32]byte
- func SymmetricOpen(key, sealed []byte) ([]byte, error)
- func SymmetricSeal(key []byte, iv [IVLen]byte, plaintext []byte) ([]byte, error)
Examples ¶
Constants ¶
const ( DomainSymmetric = "metanet keyed content symmetric v1" DomainCommitment = "metanet keyed content commitment v1" )
BRC-369 section 2's domain strings.
const ( IVLen = 32 // TagLen is the length of the authentication tag. TagLen = 16 // Overhead is what the form adds to a plaintext. Overhead = IVLen + TagLen // WrappedKeyLen is the length of a wrapped 32-byte key. WrappedKeyLen = 32 + Overhead )
The sizes of BRC-2's symmetric form as go-sdk writes it: a 32-byte IV, the ciphertext, and a 16-byte tag.
Variables ¶
var ( // ErrScalar is a key that is not a scalar in [1, n-1]. ErrScalar = errors.New("keyed: key is not a scalar in [1, n-1]") // ErrCommitment is a key that is not the committed one. ErrCommitment = errors.New("keyed: key does not match the commitment") // ErrUnwrap is a wrap that does not open, or opens to something that // is not 32 bytes. ErrUnwrap = errors.New("keyed: wrap does not open") )
A holder's refusals (BRC-369 section 5.3), never a host's: a host cannot see a key.
Functions ¶
func CheckOpened ¶
CheckOpened is the tail of every unwrap, in its order: the plaintext is exactly 32 bytes (ErrUnwrap), a scalar (ErrScalar), and the committed one (ErrCommitment). The key it returns with an error must not be used.
Example ¶
A publisher draws a key, publishes its commitment, and wraps the key to each holder. A holder that opens a wrap checks the key against the commitment before anything uses it: a wrong key then names whoever wrapped it, not whoever encrypted under it.
package main
import (
"errors"
"fmt"
"github.com/lightwebinc/bcommon/goldentest"
"github.com/lightwebinc/bcommon/keyed"
)
func main() {
// A fixed scalar so the example prints the same thing every time; a
// publisher calls keyed.SampleKey(nil).
k := goldentest.Fill(0x07)
if err := keyed.CheckScalar(k); err != nil {
fmt.Println(err)
return
}
commitment := keyed.Commitment(k)
symmetric := keyed.SymmetricKey(k)
fmt.Println("the commitment says nothing about the key that encrypts:", commitment != symmetric)
// The wrap: BRC-2's symmetric form under a key the two ends share. A
// wallet's Encrypt writes the same form with an IV it draws itself.
shared := goldentest.Fill(0x21)
wrap, err := keyed.SymmetricSeal(shared[:], goldentest.Fill(0x80), k[:])
fmt.Println("wrap:", len(wrap), "bytes", err)
opened, err := keyed.SymmetricOpen(shared[:], wrap)
if err != nil {
fmt.Println(err)
return
}
got, err := keyed.CheckOpened(opened, commitment)
fmt.Println("the committed key:", got == k, err)
// The same wrap against another commitment, and another key's wrap.
other := goldentest.Fill(0x08)
_, err = keyed.CheckOpened(opened, keyed.Commitment(other))
fmt.Println("another commitment:", errors.Is(err, keyed.ErrCommitment))
wrong := goldentest.Fill(0x22)
_, err = keyed.SymmetricOpen(wrong[:], wrap)
fmt.Println("another shared key opens it:", err == nil)
}
Output: the commitment says nothing about the key that encrypts: true wrap: 80 bytes <nil> the committed key: true <nil> another commitment: true another shared key opens it: false
func CheckScalar ¶
CheckScalar refuses, as ErrScalar, a 32-byte big-endian value outside [1, n-1].
func Commitment ¶
Commitment is BRC-369 section 2.2's digest form: SHA-256 of the domain string and the scalar.
func SampleKey ¶
SampleKey draws a key: a scalar uniform in [1, n-1] from r (crypto/rand.Reader when r is nil), never derived from anything (BRC-369 section 2.1 rule 1).
func SymmetricKey ¶
SymmetricKey is BRC-369 section 2.1 rule 3: SHA-256 of the domain string and the scalar. It is never derived from the commitment.
func SymmetricOpen ¶
SymmetricOpen reverses SymmetricSeal through go-sdk's own symmetric key, so it opens exactly what a wallet's Decrypt opens under the same key.
func SymmetricSeal ¶
SymmetricSeal is BRC-2's symmetric form as go-sdk writes it: AES-256-GCM under key with a 32-byte IV and no associated data, written IV || ciphertext || tag. It is what a wallet's Encrypt returns, with the IV chosen by the caller in place of a random one: an IV must never be used twice under one key, so only a caller that draws it from a CSPRNG, or a vector that fixes it to be reproducible, calls this.
Types ¶
This section is empty.