OSDN Git Service

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