github.com/iDigitalFlame/xmt@v0.5.4/data/crypto.go (about) 1 // Copyright (C) 2020 - 2023 iDigitalFlame 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU General Public License as published by 5 // the Free Software Foundation, either version 3 of the License, or 6 // any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU General Public License for more details. 12 // 13 // You should have received a copy of the GNU General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 // 16 17 package data 18 19 import ( 20 "crypto/elliptic" 21 "crypto/rand" 22 "io" 23 24 "github.com/iDigitalFlame/xmt/util/xerr" 25 ) 26 27 const ( 28 sharedKeySize = 65 29 publicKeySize = 133 30 privateKeySize = 66 31 ) 32 33 var keyCurve = elliptic.P521() 34 35 // KeyPair is a ECDH key pair that can be used to generate and manage Public, 36 // Private and shared keys. The data from this struct can be written and read 37 // from disk or network. 38 // 39 // The empty value can be filled using the 'Fill*' functions. 40 // 41 // If not changed via the 'keyCurve' const, this function uses NIST P-521 (FIPS 42 // 186-3, section D.2.5), known as secp521r1. 43 // 44 // Initial ideas and concepts from: https://github.com/wsddn/go-ecdh 45 type KeyPair struct { 46 Public PublicKey 47 Private PrivateKey 48 share SharedKeys 49 } 50 51 // PublicKey represents a ECDH PublicKey in raw binary format. This alias can be 52 // used to parse or output to a string value. 53 type PublicKey [publicKeySize]byte 54 55 // PrivateKey represents a ECDH PrivateKey in raw binary format. This alias can 56 // be used to parse or output to a string value. 57 type PrivateKey [privateKeySize]byte 58 59 // SharedKeys represents a ECDH SharedKey in raw binary format. This alias is just 60 // used to differentate the shared key from other binary structures. 61 type SharedKeys [sharedKeySize]byte 62 63 // Fill will populate this KeyPair with a randomally generated Public and Private 64 // key values. 65 // 66 // Before returning, this function will zero out the shared secret. 67 func (k *KeyPair) Fill() { 68 // We can ignore the error, since it's only for the read operation. 69 var ( 70 p, x, y, _ = elliptic.GenerateKey(keyCurve, rand.Reader) 71 s = elliptic.Marshal(keyCurve, x, y) 72 ) 73 copy(k.Public[:], s) 74 copy(k.Private[:], p) 75 p, x, y, s = nil, nil, nil, nil 76 for i := range k.share { 77 k.share[i] = 0 78 } 79 } 80 81 // Empty returns true if the PublicKey is empty (all zeros). 82 func (k *KeyPair) Empty() bool { 83 return k.Public.Empty() 84 } 85 86 // Sync attempts to generate the Shared key using the current KeyPair's Public 87 // and Private key values. 88 // 89 // This function returns an error if a Shared key could not be generated. 90 func (k *KeyPair) Sync() error { 91 return k.fillShared(k.Public, k.Private) 92 } 93 94 // Empty returns true if this PublicKey is empty (all zeros). 95 func (p PublicKey) Empty() bool { 96 for i := range p { 97 if p[i] > 0 { 98 return false 99 } 100 } 101 return true 102 } 103 104 // Hash returns the FNV-32 hash of this PublicKey in a uint32 format. 105 func (p PublicKey) Hash() uint32 { 106 h := uint32(2166136261) 107 for i := range p { 108 h *= 16777619 109 h ^= uint32(p[i]) 110 } 111 return h 112 } 113 114 // IsSynced returns false if the Shared key is empty (all zeros). 115 func (k *KeyPair) IsSynced() bool { 116 for i := range k.share { 117 if k.share[i] > 0 { 118 return true 119 } 120 } 121 return false 122 } 123 124 // Shared returns a copy of the current Shared key contained in this KeyPair. 125 // 126 // If 'IsSynced' returns false, the output will be a zero filled array. 127 func (k KeyPair) Shared() SharedKeys { 128 return k.share 129 } 130 131 // Read will read in the PublicKey ONLY from the supplied 'io.Reader' and fill 132 // the current KeyPair's PublicKey with the resulting data. 133 // 134 // Any errors or invalid byte lengths read will return an error. 135 func (k *KeyPair) Read(r io.Reader) error { 136 switch n, err := r.Read(k.Public[:]); { 137 case err != nil: 138 return err 139 case n != publicKeySize: 140 return io.ErrUnexpectedEOF 141 } 142 return nil 143 } 144 145 // Write will write out the PublicKey ONLY to the supplied 'io.Writer'. 146 // 147 // Any errors or invalid byte lengths written will return an error. 148 func (k *KeyPair) Write(w io.Writer) error { 149 switch n, err := w.Write(k.Public[:]); { 150 case err != nil: 151 return err 152 case n != publicKeySize: 153 return io.ErrShortWrite 154 } 155 return nil 156 } 157 158 // Marshal will write out the Public, Private and Shared key data to the supplied 159 // 'io.Writer'. 160 // 161 // Any errors or invalid byte lengths written will return an error. 162 func (k *KeyPair) Marshal(w io.Writer) error { 163 switch n, err := w.Write(k.Public[:]); { 164 case err != nil: 165 return err 166 case n != publicKeySize: 167 return io.ErrShortWrite 168 } 169 switch n, err := w.Write(k.Private[:]); { 170 case err != nil: 171 return err 172 case n != privateKeySize: 173 return io.ErrShortWrite 174 } 175 switch n, err := w.Write(k.share[:]); { 176 case err != nil: 177 return err 178 case n != sharedKeySize: 179 return io.ErrShortWrite 180 } 181 return nil 182 } 183 184 // Unmarshal will read in the Public, Private and Shared key data from the supplied 185 // 'io.Reader' and fill all the current KeyPair data with the resulting data. 186 // 187 // Any errors or invalid byte lengths read will return an error. 188 func (k *KeyPair) Unmarshal(r io.Reader) error { 189 switch n, err := r.Read(k.Public[:]); { 190 case err != nil: 191 return err 192 case n != publicKeySize: 193 return io.ErrUnexpectedEOF 194 } 195 switch n, err := r.Read(k.Private[:]); { 196 case err != nil: 197 return err 198 case n != privateKeySize: 199 return io.ErrUnexpectedEOF 200 } 201 switch n, err := r.Read(k.share[:]); { 202 case err != nil: 203 return err 204 case n != sharedKeySize: 205 return io.ErrUnexpectedEOF 206 } 207 return nil 208 } 209 210 // FillPublic will generate the Shared key using the KeyPair's PrivateKey and 211 // the supplied PublicKey. 212 // 213 // If successful, the PublicKey data will be copied over the current KeyPair's 214 // PublicKey for successive calls to 'Sync'. 215 // 216 // This function returns an error if a Shared key could not be generated. 217 func (k *KeyPair) FillPublic(p PublicKey) error { 218 if err := k.fillShared(p, k.Private); err != nil { 219 return err 220 } 221 copy(k.Public[:], p[:]) 222 return nil 223 } 224 225 // FillPrivate will generate the Shared key using the KeyPair's PublicKey and 226 // the supplied PrivateKey. 227 // 228 // If successful, the PrivateKey data will be copied over the current KeyPair's 229 // PrivateKey for successive calls to 'Sync'. 230 // 231 // This function returns an error if a Shared key could not be generated. 232 func (k *KeyPair) FillPrivate(p PrivateKey) error { 233 if err := k.fillShared(k.Public, p); err != nil { 234 return err 235 } 236 copy(k.Private[:], p[:]) 237 return nil 238 } 239 func (k *KeyPair) fillShared(n PublicKey, m PrivateKey) error { 240 x, y := elliptic.Unmarshal(keyCurve, n[:]) 241 if x == nil || y == nil { 242 return xerr.Sub("cannot parse curve PublicKey", 0x77) 243 } 244 v, _ := keyCurve.ScalarMult(x, y, m[:]) 245 if x, y = nil, nil; v == nil { 246 return xerr.Sub("cannot multiply PrivateKey with PublicKey", 0x78) 247 } 248 copy(k.share[:], v.Bytes()) 249 x = nil 250 return nil 251 }