github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/third_party/code.google.com/p/go.crypto/openpgp/s2k/s2k.go (about) 1 // Copyright 2011 The Go Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package s2k implements the various OpenPGP string-to-key transforms as 6 // specified in RFC 4800 section 3.7.1. 7 package s2k 8 9 import ( 10 "camlistore.org/third_party/code.google.com/p/go.crypto/openpgp/errors" 11 "crypto" 12 "hash" 13 "io" 14 "strconv" 15 ) 16 17 // Simple writes to out the result of computing the Simple S2K function (RFC 18 // 4880, section 3.7.1.1) using the given hash and input passphrase. 19 func Simple(out []byte, h hash.Hash, in []byte) { 20 Salted(out, h, in, nil) 21 } 22 23 var zero [1]byte 24 25 // Salted writes to out the result of computing the Salted S2K function (RFC 26 // 4880, section 3.7.1.2) using the given hash, input passphrase and salt. 27 func Salted(out []byte, h hash.Hash, in []byte, salt []byte) { 28 done := 0 29 var digest []byte 30 31 for i := 0; done < len(out); i++ { 32 h.Reset() 33 for j := 0; j < i; j++ { 34 h.Write(zero[:]) 35 } 36 h.Write(salt) 37 h.Write(in) 38 digest = h.Sum(digest[:0]) 39 n := copy(out[done:], digest) 40 done += n 41 } 42 } 43 44 // Iterated writes to out the result of computing the Iterated and Salted S2K 45 // function (RFC 4880, section 3.7.1.3) using the given hash, input passphrase, 46 // salt and iteration count. 47 func Iterated(out []byte, h hash.Hash, in []byte, salt []byte, count int) { 48 combined := make([]byte, len(in)+len(salt)) 49 copy(combined, salt) 50 copy(combined[len(salt):], in) 51 52 if count < len(combined) { 53 count = len(combined) 54 } 55 56 done := 0 57 var digest []byte 58 for i := 0; done < len(out); i++ { 59 h.Reset() 60 for j := 0; j < i; j++ { 61 h.Write(zero[:]) 62 } 63 written := 0 64 for written < count { 65 if written+len(combined) > count { 66 todo := count - written 67 h.Write(combined[:todo]) 68 written = count 69 } else { 70 h.Write(combined) 71 written += len(combined) 72 } 73 } 74 digest = h.Sum(digest[:0]) 75 n := copy(out[done:], digest) 76 done += n 77 } 78 } 79 80 // Parse reads a binary specification for a string-to-key transformation from r 81 // and returns a function which performs that transform. 82 func Parse(r io.Reader) (f func(out, in []byte), err error) { 83 var buf [9]byte 84 85 _, err = io.ReadFull(r, buf[:2]) 86 if err != nil { 87 return 88 } 89 90 hash, ok := HashIdToHash(buf[1]) 91 if !ok { 92 return nil, errors.UnsupportedError("hash for S2K function: " + strconv.Itoa(int(buf[1]))) 93 } 94 h := hash.New() 95 if h == nil { 96 return nil, errors.UnsupportedError("hash not available: " + strconv.Itoa(int(hash))) 97 } 98 99 switch buf[0] { 100 case 1: 101 f := func(out, in []byte) { 102 Simple(out, h, in) 103 } 104 return f, nil 105 case 2: 106 _, err = io.ReadFull(r, buf[:8]) 107 if err != nil { 108 return 109 } 110 f := func(out, in []byte) { 111 Salted(out, h, in, buf[:8]) 112 } 113 return f, nil 114 case 3: 115 _, err = io.ReadFull(r, buf[:9]) 116 if err != nil { 117 return 118 } 119 count := (16 + int(buf[8]&15)) << (uint32(buf[8]>>4) + 6) 120 f := func(out, in []byte) { 121 Iterated(out, h, in, buf[:8], count) 122 } 123 return f, nil 124 } 125 126 return nil, errors.UnsupportedError("S2K function") 127 } 128 129 // Serialize salts and stretches the given passphrase and writes the resulting 130 // key into key. It also serializes an S2K descriptor to w. 131 func Serialize(w io.Writer, key []byte, rand io.Reader, passphrase []byte) error { 132 var buf [11]byte 133 buf[0] = 3 /* iterated and salted */ 134 buf[1], _ = HashToHashId(crypto.SHA1) 135 salt := buf[2:10] 136 if _, err := io.ReadFull(rand, salt); err != nil { 137 return err 138 } 139 const count = 65536 // this is the default in gpg 140 buf[10] = 96 // 65536 iterations 141 if _, err := w.Write(buf[:]); err != nil { 142 return err 143 } 144 145 Iterated(key, crypto.SHA1.New(), passphrase, salt, count) 146 return nil 147 } 148 149 // hashToHashIdMapping contains pairs relating OpenPGP's hash identifier with 150 // Go's crypto.Hash type. See RFC 4880, section 9.4. 151 var hashToHashIdMapping = []struct { 152 id byte 153 hash crypto.Hash 154 }{ 155 {1, crypto.MD5}, 156 {2, crypto.SHA1}, 157 {3, crypto.RIPEMD160}, 158 {8, crypto.SHA256}, 159 {9, crypto.SHA384}, 160 {10, crypto.SHA512}, 161 {11, crypto.SHA224}, 162 } 163 164 // HashIdToHash returns a crypto.Hash which corresponds to the given OpenPGP 165 // hash id. 166 func HashIdToHash(id byte) (h crypto.Hash, ok bool) { 167 for _, m := range hashToHashIdMapping { 168 if m.id == id { 169 return m.hash, true 170 } 171 } 172 return 0, false 173 } 174 175 // HashIdToHash returns an OpenPGP hash id which corresponds the given Hash. 176 func HashToHashId(h crypto.Hash) (id byte, ok bool) { 177 for _, m := range hashToHashIdMapping { 178 if m.hash == h { 179 return m.id, true 180 } 181 } 182 return 0, false 183 }