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  }