main
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}