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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
|
// Package eddilithium3 implements the hybrid signature scheme Ed448-Dilithium3.
package eddilithium3
import (
"crypto"
cryptoRand "crypto/rand"
"errors"
"io"
"github.com/cloudflare/circl/internal/sha3"
"github.com/cloudflare/circl/sign"
"github.com/cloudflare/circl/sign/dilithium/mode3"
"github.com/cloudflare/circl/sign/ed448"
)
const (
// SeedSize is the length of the seed for NewKeyFromSeed
SeedSize = ed448.SeedSize // > mode3.SeedSize
// PublicKeySize is the length in bytes of the packed public key.
PublicKeySize = mode3.PublicKeySize + ed448.PublicKeySize
// PrivateKeySize is the length in bytes of the packed public key.
PrivateKeySize = mode3.PrivateKeySize + ed448.SeedSize
// SignatureSize is the length in bytes of the signatures.
SignatureSize = mode3.SignatureSize + ed448.SignatureSize
)
// PublicKey is the type of an EdDilithium3 public key.
type PublicKey struct {
e ed448.PublicKey
d mode3.PublicKey
}
// PrivateKey is the type of an EdDilithium3 private key.
type PrivateKey struct {
e ed448.PrivateKey
d mode3.PrivateKey
}
// GenerateKey generates a public/private key pair using entropy from rand.
// If rand is nil, crypto/rand.Reader will be used.
func GenerateKey(rand io.Reader) (*PublicKey, *PrivateKey, error) {
var seed [SeedSize]byte
if rand == nil {
rand = cryptoRand.Reader
}
_, err := io.ReadFull(rand, seed[:])
if err != nil {
return nil, nil, err
}
pk, sk := NewKeyFromSeed(&seed)
return pk, sk, nil
}
// NewKeyFromSeed derives a public/private key pair using the given seed.
func NewKeyFromSeed(seed *[SeedSize]byte) (*PublicKey, *PrivateKey) {
var seed1 [32]byte
var seed2 [ed448.SeedSize]byte
h := sha3.NewShake256()
_, _ = h.Write(seed[:])
_, _ = h.Read(seed1[:])
_, _ = h.Read(seed2[:])
dpk, dsk := mode3.NewKeyFromSeed(&seed1)
esk := ed448.NewKeyFromSeed(seed2[:])
return &PublicKey{esk.Public().(ed448.PublicKey), *dpk}, &PrivateKey{esk, *dsk}
}
// SignTo signs the given message and writes the signature into signature.
// It will panic if signature is not of length at least SignatureSize.
func SignTo(sk *PrivateKey, msg []byte, signature []byte) {
mode3.SignTo(
&sk.d,
msg,
signature[:mode3.SignatureSize],
)
esig := ed448.Sign(
sk.e,
msg,
"",
)
copy(signature[mode3.SignatureSize:], esig[:])
}
// Verify checks whether the given signature by pk on msg is valid.
func Verify(pk *PublicKey, msg []byte, signature []byte) bool {
if !mode3.Verify(
&pk.d,
msg,
signature[:mode3.SignatureSize],
) {
return false
}
if !ed448.Verify(
pk.e,
msg,
signature[mode3.SignatureSize:],
"",
) {
return false
}
return true
}
// Unpack unpacks pk to the public key encoded in buf.
func (pk *PublicKey) Unpack(buf *[PublicKeySize]byte) {
var tmp [mode3.PublicKeySize]byte
copy(tmp[:], buf[:mode3.PublicKeySize])
pk.d.Unpack(&tmp)
pk.e = make([]byte, ed448.PublicKeySize)
copy(pk.e, buf[mode3.PublicKeySize:])
}
// Unpack sets sk to the private key encoded in buf.
func (sk *PrivateKey) Unpack(buf *[PrivateKeySize]byte) {
var tmp [mode3.PrivateKeySize]byte
copy(tmp[:], buf[:mode3.PrivateKeySize])
sk.d.Unpack(&tmp)
sk.e = ed448.NewKeyFromSeed(buf[mode3.PrivateKeySize:])
}
// Pack packs the public key into buf.
func (pk *PublicKey) Pack(buf *[PublicKeySize]byte) {
var tmp [mode3.PublicKeySize]byte
pk.d.Pack(&tmp)
copy(buf[:mode3.PublicKeySize], tmp[:])
copy(buf[mode3.PublicKeySize:], pk.e)
}
// Pack packs the private key into buf.
func (sk *PrivateKey) Pack(buf *[PrivateKeySize]byte) {
var tmp [mode3.PrivateKeySize]byte
sk.d.Pack(&tmp)
copy(buf[:mode3.PrivateKeySize], tmp[:])
copy(buf[mode3.PrivateKeySize:], sk.e.Seed())
}
// Bytes packs the public key.
func (pk *PublicKey) Bytes() []byte {
return append(pk.d.Bytes(), pk.e...)
}
// Bytes packs the private key.
func (sk *PrivateKey) Bytes() []byte {
return append(sk.d.Bytes(), sk.e.Seed()...)
}
// MarshalBinary packs the public key.
func (pk *PublicKey) MarshalBinary() ([]byte, error) {
return pk.Bytes(), nil
}
// MarshalBinary packs the private key.
func (sk *PrivateKey) MarshalBinary() ([]byte, error) {
return sk.Bytes(), nil
}
// UnmarshalBinary the public key from data.
func (pk *PublicKey) UnmarshalBinary(data []byte) error {
if len(data) != PublicKeySize {
return errors.New("packed public key must be of eddilithium3.PublicKeySize bytes")
}
var buf [PublicKeySize]byte
copy(buf[:], data)
pk.Unpack(&buf)
return nil
}
// UnmarshalBinary unpacks the private key from data.
func (sk *PrivateKey) UnmarshalBinary(data []byte) error {
if len(data) != PrivateKeySize {
return errors.New("packed private key must be of eddilithium3.PrivateKeySize bytes")
}
var buf [PrivateKeySize]byte
copy(buf[:], data)
sk.Unpack(&buf)
return nil
}
func (sk *PrivateKey) Scheme() sign.Scheme { return sch }
func (pk *PublicKey) Scheme() sign.Scheme { return sch }
func (sk *PrivateKey) Equal(other crypto.PrivateKey) bool {
castOther, ok := other.(*PrivateKey)
if !ok {
return false
}
return castOther.e.Equal(sk.e) && castOther.d.Equal(&sk.d)
}
func (pk *PublicKey) Equal(other crypto.PublicKey) bool {
castOther, ok := other.(*PublicKey)
if !ok {
return false
}
return castOther.e.Equal(pk.e) && castOther.d.Equal(&pk.d)
}
// Sign signs the given message.
//
// opts.HashFunc() must return zero, which can be achieved by passing
// crypto.Hash(0) for opts. rand is ignored. Will only return an error
// if opts.HashFunc() is non-zero.
//
// This function is used to make PrivateKey implement the crypto.Signer
// interface. The package-level SignTo function might be more convenient
// to use.
func (sk *PrivateKey) Sign(
rand io.Reader, msg []byte, opts crypto.SignerOpts,
) (signature []byte, err error) {
var sig [SignatureSize]byte
if opts.HashFunc() != crypto.Hash(0) {
return nil, errors.New("eddilithium3: cannot sign hashed message")
}
SignTo(sk, msg, sig[:])
return sig[:], nil
}
// Public computes the public key corresponding to this private key.
//
// Returns a *PublicKey. The type crypto.PublicKey is used to make
// PrivateKey implement the crypto.Signer interface.
func (sk *PrivateKey) Public() crypto.PublicKey {
return &PublicKey{
sk.e.Public().(ed448.PublicKey),
*sk.d.Public().(*mode3.PublicKey),
}
}
|