Commit fd74a835 authored by Adam Langley's avatar Adam Langley

Add initial x509 code.

R=rsc
APPROVED=rsc
DELTA=659  (659 added, 0 deleted, 0 changed)
OCL=35932
CL=35975
parent d7ad3c4e
......@@ -27,6 +27,7 @@ encoding/ascii85.install: bytes.install io.install os.install strconv.install
encoding/base64.install: bytes.install io.install os.install strconv.install
encoding/binary.install: io.install math.install os.install reflect.install
encoding/git85.install: bytes.install io.install os.install strconv.install
encoding/pem.install: bytes.install encoding/base64.install strings.install
exec.install: os.install strings.install
exp/datafmt.install: bytes.install container/vector.install fmt.install go/scanner.install go/token.install io.install os.install reflect.install runtime.install strconv.install strings.install
exp/eval.install: bignum.install fmt.install go/ast.install go/parser.install go/scanner.install go/token.install log.install os.install reflect.install runtime.install strconv.install strings.install
......
......@@ -41,6 +41,7 @@ DIRS=\
encoding/base64\
encoding/binary\
encoding/git85\
encoding/pem\
exec\
exp/datafmt\
exp/eval\
......
......@@ -97,6 +97,43 @@ type PrivateKey struct {
P, Q *big.Int; // prime factors of N
}
// Validate performs basic sanity checks on the key.
// It returns nil if the key is valid, or else an os.Error describing a problem.
func (priv PrivateKey) Validate() os.Error {
// Check that p and q are prime.
if !priv.P.ProbablyPrime(20) {
return os.ErrorString("P is composite");
}
if !priv.Q.ProbablyPrime(20) {
return os.ErrorString("Q is composite");
}
// Check that p*q == n.
modulus := new(big.Int).Mul(priv.P, priv.Q);
if big.CmpInt(modulus, priv.N) != 0 {
return os.ErrorString("invalid modulus");
}
// Check that e and totient(p, q) are coprime.
pminus1 := new(big.Int).Sub(priv.P, bigOne);
qminus1 := new(big.Int).Sub(priv.Q, bigOne);
totient := new(big.Int).Mul(pminus1, qminus1);
e := big.NewInt(int64(priv.E));
gcd := new(big.Int);
x := new(big.Int);
y := new(big.Int);
big.GcdInt(gcd, x, y, totient, e);
if big.CmpInt(gcd, bigOne) != 0 {
return os.ErrorString("invalid public exponent E");
}
// Check that de ≡ 1 (mod totient(p, q))
de := new(big.Int).Mul(priv.D, e);
de.Mod(de, totient);
if big.CmpInt(de, bigOne) != 0 {
return os.ErrorString("invalid private exponent D");
}
return nil;
}
// GenerateKeyPair generates an RSA keypair of the given bit size.
func GenerateKey(rand io.Reader, bits int) (priv *PrivateKey, err os.Error) {
priv = new(PrivateKey);
......
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.$(GOARCH)
TARG=crypto/x509
GOFILES=\
x509.go\
include $(GOROOT)/src/Make.pkg
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// NOTE: PACKAGE UNDER CONSTRUCTION.
//
// This package parses X.509-encoded keys and certificates.
package x509
import (
"asn1";
"crypto/rsa";
"os";
big "gmp";
)
// pkcs1PrivateKey is a structure which mirrors the PKCS#1 ASN.1 for an RSA private key.
type pkcs1PrivateKey struct {
Version int;
N asn1.RawValue;
E int;
D asn1.RawValue;
P asn1.RawValue;
Q asn1.RawValue;
}
// rawValueIsInteger returns true iff the given ASN.1 RawValue is an INTEGER type.
func rawValueIsInteger(raw *asn1.RawValue) bool {
return raw.Class == 0 && raw.Tag == 2 && raw.IsCompound == false;
}
// ParsePKCS1PrivateKey returns an RSA private key from its ASN.1 PKCS#1 DER encoded form.
func ParsePKCS1PrivateKey(der []byte) (key *rsa.PrivateKey, err os.Error) {
var priv pkcs1PrivateKey;
err = asn1.Unmarshal(&priv, der);
if err != nil {
return;
}
if !rawValueIsInteger(&priv.N) ||
!rawValueIsInteger(&priv.D) ||
!rawValueIsInteger(&priv.P) ||
!rawValueIsInteger(&priv.Q) {
err = asn1.StructuralError{"tags don't match"};
return;
}
key = &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
E: priv.E,
N: new(big.Int).SetBytes(priv.N.Bytes),
},
D: new(big.Int).SetBytes(priv.D.Bytes),
P: new(big.Int).SetBytes(priv.P.Bytes),
Q: new(big.Int).SetBytes(priv.Q.Bytes),
};
err = key.Validate();
if err != nil {
return nil, err;
}
return;
}
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package x509
import (
"crypto/rsa";
"encoding/pem";
"reflect";
"strings";
"testing";
big "gmp";
)
func TestParsePKCS1PrivateKey(t *testing.T) {
block, _ := pem.Decode(strings.Bytes(pemPrivateKey));
priv, err := ParsePKCS1PrivateKey(block.Bytes);
if err != nil {
t.Errorf("Failed to parse private key: %s", err);
}
if !reflect.DeepEqual(priv, rsaPrivateKey) {
t.Errorf("got:%+v want:%+v", priv, rsaPrivateKey);
}
}
var pemPrivateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
-----END RSA PRIVATE KEY-----
`
func bigFromString(s string) *big.Int {
ret := new(big.Int);
ret.SetString(s, 10);
return ret;
}
var rsaPrivateKey = &rsa.PrivateKey{
PublicKey: rsa.PublicKey{
N: bigFromString("9353930466774385905609975137998169297361893554149986716853295022578535724979677252958524466350471210367835187480748268864277464700638583474144061408845077"),
E: 65537,
},
D: bigFromString("7266398431328116344057699379749222532279343923819063639497049039389899328538543087657733766554155839834519529439851673014800261285757759040931985506583861"),
P: bigFromString("98920366548084643601728869055592650835572950932266967461790948584315647051443"),
Q: bigFromString("94560208308847015747498523884063394671606671904944666360068158221458669711639"),
}
# Copyright 2009 The Go Authors. All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
include $(GOROOT)/src/Make.$(GOARCH)
TARG=encoding/pem
GOFILES=\
pem.go\
include $(GOROOT)/src/Make.pkg
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This package implements the PEM data encoding, which originated in Privacy
// Enhanced Mail. The most common use of PEM encoding today is in TLS keys and
// certificates. See RFC 1421.
package pem
import (
"bytes";
"encoding/base64";
"strings";
)
// A Block represents a PEM encoded structure.
//
// The encoded form is:
// -----BEGIN Type-----
// Headers
// base64-encoded Bytes
// -----END Type-----
// where Headers is a possibly empty sequence of Key: Value lines.
type Block struct {
Type string; // The type, taken from the preamble (i.e. "RSA PRIVATE KEY").
Headers map[string]string; // Optional headers.
Bytes []byte; // The decoded bytes of the contents. Typically a DER encoded ASN.1 structure.
}
// getLine results the first \r\n or \n delineated line from the given byte
// array. The line does not include the \r\n or \n. The remainder of the byte
// array (also not including the new line bytes) is also returned and this will
// always be smaller than the original argument.
func getLine(data []byte) (line, rest []byte) {
i := bytes.Index(data, []byte{'\n'});
var j int;
if i < 0 {
i = len(data);
j = i;
} else {
j = i+1;
if i > 0 && data[i-1] == '\r' {
i--;
}
}
return data[0:i], data[j:len(data)];
}
// removeWhitespace returns a copy of its input with all spaces, tab and
// newline characters removed.
func removeWhitespace(data []byte) []byte {
result := make([]byte, len(data));
n := 0;
for _, b := range data {
if b == ' ' || b == '\t' || b == '\r' || b == '\n' {
continue;
}
result[n] = b;
n++;
}
return result[0:n];
}
var pemStart = strings.Bytes("\n-----BEGIN ")
var pemEnd = strings.Bytes("\n-----END ")
var pemEndOfLine = strings.Bytes("-----")
// Decode will find the next PEM formatted block (certificate, private key
// etc) in the input. It returns that block and the remainder of the input. If
// no PEM data is found, p is nil and the whole of the input is returned in // rest.
func Decode(data []byte) (p *Block, rest []byte) {
// pemStart begins with a newline. However, at the very beginning of
// the byte array, we'll accept the start string without it.
rest = data;
if bytes.HasPrefix(data, pemStart[1:len(pemStart)]) {
rest = rest[len(pemStart)-1 : len(data)];
} else if i := bytes.Index(data, pemStart); i >= 0 {
rest = rest[i+len(pemStart) : len(data)];
} else {
return nil, data;
}
typeLine, rest := getLine(rest);
if !bytes.HasSuffix(typeLine, pemEndOfLine) {
goto Error;
}
typeLine = typeLine[0 : len(typeLine)-len(pemEndOfLine)];
p = &Block{
Headers: make(map[string]string),
Type: string(typeLine),
};
for {
// This loop terminates because getLine's second result is
// always smaller than it's argument.
if len(rest) == 0 {
return nil, data;
}
line, next := getLine(rest);
i := bytes.Index(line, []byte{':'});
if i == -1 {
break;
}
// TODO(agl): need to cope with values that spread across lines.
key, val := line[0:i], line[i+1 : len(line)];
key = bytes.TrimSpace(key);
val = bytes.TrimSpace(val);
p.Headers[string(key)] = string(val);
rest = next;
}
i := bytes.Index(rest, pemEnd);
if i < 0 {
goto Error;
}
base64Data := removeWhitespace(rest[0:i]);
p.Bytes = make([]byte, base64.StdEncoding.DecodedLen(len(base64Data)));
n, err := base64.StdEncoding.Decode(base64Data, p.Bytes);
if err != nil {
goto Error;
}
p.Bytes = p.Bytes[0:n];
_, rest = getLine(rest[i+len(pemEnd) : len(rest)]);
return;
Error:
// If we get here then we have rejected a likely looking, but
// ultimately invalid PEM block. We need to start over from a new
// position. We have consumed the preamble line and will have consumed
// any lines which could be header lines. However, a valid preamble
// line is not a valid header line, therefore we cannot have consumed
// the preamble line for the any subsequent block. Thus, we will always
// find any valid block, no matter what bytes preceed it.
//
// For example, if the input is
//
// -----BEGIN MALFORMED BLOCK-----
// junk that may look like header lines
// or data lines, but no END line
//
// -----BEGIN ACTUAL BLOCK-----
// realdata
// -----END ACTUAL BLOCK-----
//
// we've failed to parse using the first BEGIN line
// and now will try again, using the second BEGIN line.
p, rest = Decode(rest);
if p == nil {
rest = data;
}
return;
}
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment