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