main
Raw Download raw file
  1// Copyright 2014 Matthew Endsley
  2// All rights reserved
  3//
  4// Redistribution and use in source and binary forms, with or without
  5// modification, are permitted providing that the following conditions
  6// are met:
  7// 1. Redistributions of source code must retain the above copyright
  8//    notice, this list of conditions and the following disclaimer.
  9// 2. Redistributions in binary form must reproduce the above copyright
 10//    notice, this list of conditions and the following disclaimer in the
 11//    documentation and/or other materials provided with the distribution.
 12//
 13// THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 14// IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 15// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 16// ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 17// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 18// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 19// OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 20// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 21// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 22// IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 23// POSSIBILITY OF SUCH DAMAGE.
 24
 25// Package keywrap is an implementation of the RFC 3394 AES key wrapping
 26// algorithm. This is used in OpenPGP with elliptic curve keys.
 27package keywrap
 28
 29import (
 30	"crypto/aes"
 31	"encoding/binary"
 32	"errors"
 33)
 34
 35var (
 36	// ErrWrapPlaintext is returned if the plaintext is not a multiple
 37	// of 64 bits.
 38	ErrWrapPlaintext = errors.New("keywrap: plainText must be a multiple of 64 bits")
 39
 40	// ErrUnwrapCiphertext is returned if the ciphertext is not a
 41	// multiple of 64 bits.
 42	ErrUnwrapCiphertext = errors.New("keywrap: cipherText must by a multiple of 64 bits")
 43
 44	// ErrUnwrapFailed is returned if unwrapping a key fails.
 45	ErrUnwrapFailed = errors.New("keywrap: failed to unwrap key")
 46
 47	// NB: the AES NewCipher call only fails if the key is an invalid length.
 48
 49	// ErrInvalidKey is returned when the AES key is invalid.
 50	ErrInvalidKey = errors.New("keywrap: invalid AES key")
 51)
 52
 53// Wrap a key using the RFC 3394 AES Key Wrap Algorithm.
 54func Wrap(key, plainText []byte) ([]byte, error) {
 55	if len(plainText)%8 != 0 {
 56		return nil, ErrWrapPlaintext
 57	}
 58
 59	c, err := aes.NewCipher(key)
 60	if err != nil {
 61		return nil, ErrInvalidKey
 62	}
 63
 64	nblocks := len(plainText) / 8
 65
 66	// 1) Initialize variables.
 67	var block [aes.BlockSize]byte
 68	// - Set A = IV, an initial value (see 2.2.3)
 69	for ii := 0; ii < 8; ii++ {
 70		block[ii] = 0xA6
 71	}
 72
 73	// - For i = 1 to n
 74	// -   Set R[i] = P[i]
 75	intermediate := make([]byte, len(plainText))
 76	copy(intermediate, plainText)
 77
 78	// 2) Calculate intermediate values.
 79	for ii := 0; ii < 6; ii++ {
 80		for jj := 0; jj < nblocks; jj++ {
 81			// - B = AES(K, A | R[i])
 82			copy(block[8:], intermediate[jj*8:jj*8+8])
 83			c.Encrypt(block[:], block[:])
 84
 85			// - A = MSB(64, B) ^ t where t = (n*j)+1
 86			t := uint64(ii*nblocks + jj + 1)
 87			val := binary.BigEndian.Uint64(block[:8]) ^ t
 88			binary.BigEndian.PutUint64(block[:8], val)
 89
 90			// - R[i] = LSB(64, B)
 91			copy(intermediate[jj*8:jj*8+8], block[8:])
 92		}
 93	}
 94
 95	// 3) Output results.
 96	// - Set C[0] = A
 97	// - For i = 1 to n
 98	// -   C[i] = R[i]
 99	return append(block[:8], intermediate...), nil
100}
101
102// Unwrap a key using the RFC 3394 AES Key Wrap Algorithm.
103func Unwrap(key, cipherText []byte) ([]byte, error) {
104	if len(cipherText)%8 != 0 {
105		return nil, ErrUnwrapCiphertext
106	}
107
108	c, err := aes.NewCipher(key)
109	if err != nil {
110		return nil, ErrInvalidKey
111	}
112
113	nblocks := len(cipherText)/8 - 1
114
115	// 1) Initialize variables.
116	var block [aes.BlockSize]byte
117	// - Set A = C[0]
118	copy(block[:8], cipherText[:8])
119
120	// - For i = 1 to n
121	// -   Set R[i] = C[i]
122	intermediate := make([]byte, len(cipherText)-8)
123	copy(intermediate, cipherText[8:])
124
125	// 2) Compute intermediate values.
126	for jj := 5; jj >= 0; jj-- {
127		for ii := nblocks - 1; ii >= 0; ii-- {
128			// - B = AES-1(K, (A ^ t) | R[i]) where t = n*j+1
129			// - A = MSB(64, B)
130			t := uint64(jj*nblocks + ii + 1)
131			val := binary.BigEndian.Uint64(block[:8]) ^ t
132			binary.BigEndian.PutUint64(block[:8], val)
133
134			copy(block[8:], intermediate[ii*8:ii*8+8])
135			c.Decrypt(block[:], block[:])
136
137			// - R[i] = LSB(B, 64)
138			copy(intermediate[ii*8:ii*8+8], block[8:])
139		}
140	}
141
142	// 3) Output results.
143	// - If A is an appropriate initial value (see 2.2.3),
144	for ii := 0; ii < 8; ii++ {
145		if block[ii] != 0xA6 {
146			return nil, ErrUnwrapFailed
147		}
148	}
149
150	// - For i = 1 to n
151	// -   P[i] = R[i]
152	return intermediate, nil
153}